【问题标题】:Write getters as a prototype编写 getter 作为原型
【发布时间】:2015-08-18 17:40:34
【问题描述】:

我正在对我的 javascript 代码进行性能更新。

在 Firefox 中我收到了这个警告:

改变对象的 [[Prototype]] 会导致您的代码运行非常缓慢;而是使用 Object.create 使用正确的初始 [[Prototype]] 值创建对象

我编写了一些脚本来证明这一点,结果非常好:没有突变,一个简单的脚本运行速度提高了 66%。

但是我无法在没有突变的情况下转换我的代码,我无法编写 getter:

这就是我现在拥有的:

// Class
function FooBar(options) {
  this.options = options;
}

// Prototype
FooBar.prototype = {

  // Getters
  get a() {
      return this.options.a;
    },

    get b() {
      return this.options.b;
    },

    get ab() {
      return this.options.a + this.options.b;
    },

    // Methods
    displayOptions: function() {
      console.log(this.options);
    }
};

// Code
var options = {
  a: 'foo',
  b: 'bar'
};

var fooBar = new FooBar(options);

console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();

在返回时使用 this 关键字作为原型的 getter 是问题所在。

如果我使用 Object.defineProperty,this 关键字是错误的,除非我在构造函数中这样做,但它会在类的每个实例上重新创建属性并进一步减慢我的代码。

【问题讨论】:

  • “如果我使用 Object.defineProperty,则 this 关键字是错误的。” - 不,不是。它在两种情况下的行为都是相同的,具体取决于接收者对象。 “..但它会在类的每个实例上重新创建属性” - 可能不是,但这取决于它的含义。我真的不明白这段代码与“改变 [[Prototype]]”有什么关系..
  • 确实我一直想知道为什么MDN says that。这完全是错误的。无论如何,在您的代码中,FooBar.prototype 不会更改 FooBar 的原型,而只会更改该构造函数创建的新对象的原型。
  • @user2864740 ""如果我使用 Object.defineProperty,this 关键字是错误的......" - 不,不是。"你是对的,它不是。我在尝试时弄乱了语法。这是突变:看这个线程:stackoverflow.com/questions/23807805/…

标签: javascript prototype setter getter mutation


【解决方案1】:

这行得通(我在之前的尝试中只是弄乱了语法):

// Class
function FooBar (options) {
	this.options = options;
}

//Prototype getters
Object.defineProperty(FooBar.prototype, 'a', {
	get: function() {
		return this.options.a;
	}
});

Object.defineProperty(FooBar.prototype, 'b', {
	get: function() {
		return this.options.b;
	}
});


Object.defineProperty(FooBar.prototype, 'ab', {
	get: function() {
		return this.options.a + this.options.b;
	}
});

// Methods
FooBar.prototype.displayOptions = function() {
	console.log(this.options);
};

// Code
var options = {
	a:'foo',
	b:'bar'
};

var fooBar = new FooBar (options);

console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();

对于那些对将这样的脚本转换为更快的好处感到好奇的人:运行以下代码并在控制台中查看您的输出(Chrome - 快 66%,Firefox - 没有区别(好奇,因为我收到了警告来自火狐)):

// WITHOUT PROTOTYPING
var Person1 = function() {
	this.name = 'myName';
	this.changeName = function(name) {
		this.name = name;
	};
	this.changeName2 = function(name) {
		this.name = name;
	};
	this.changeName3 = function(name) {
		this.name = name;
	};
	this.changeName4 = function(name) {
		this.name = name;
	};
}


// WITH PROTOTYPING, WITH MUTATION
var Person2 = function() {
	this.name = 'myName';
}
Person2.prototype = {
	changeName: function(name) {
		this.name = name;
	},
	changeName2: function(name) {
		this.name = name;
	},
	changeName3: function(name) {
		this.name = name;
	},
	changeName4: function(name) {
		this.name = name;
	}
};

// WITH PROTOTYPING, WITHOUT MUTATION
var Person3 = function() {
	this.name = 'myName';
}
Person3.prototype.changeName = function(name) {
	this.name = name;
};
Person3.prototype.changeName2 = function(name) {
	this.name = name;
};
Person3.prototype.changeName3 = function(name) {
	this.name = name;
};
Person3.prototype.changeName4 = function(name) {
	this.name = name;
};


// DO THE TEST
var i=0, len=1000000;

// TEST1
window.performance.mark('mark_test_start');
for(i=0;i<len;i++) {
	p = new Person1();
	p.changeName('myName2');
}
window.performance.mark('mark_test_end');
window.performance.measure('no-prototyping', 'mark_test_start', 'mark_test_end');


// TEST2
window.performance.mark('mark_test2_start');
for(i=0;i<len;i++) {
	p = new Person2();
	p.changeName('myName2');
}
window.performance.mark('mark_test2_end');
window.performance.measure('prototyping-with-mutation', 'mark_test2_start', 'mark_test2_end');

// TEST3
window.performance.mark('mark_test3_start');
for(i=0;i<len;i++) {
	p = new Person2();
	p.changeName('myName2');
}
window.performance.mark('mark_test3_end');
window.performance.measure('prototyping-without-mutation', 'mark_test3_start', 'mark_test3_end');



// OUTPUT tests
var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log(req.name + ': ' + req.duration.toFixed(2));
}

【讨论】:

    猜你喜欢
    • 2019-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多