但是通过这种方式会导致对象的[[Enumerable]]特性被设置为ture,默认情况下,constructor属性时不可枚举的,可以通过Object.defineProperty()解决这个问题。
// 构造函数 function Person() {}; Person.prototype = { sayHi: function() { console.log(hi); }, name: '张三', }; Object.defineProperty(Person.prototype, "constructor", { enumerable: false, value: Person }
4.原型的动态性
当对原型对象所做的任何修改都能够立即从实例上反应出来。
function Person() {}; var friend = new Person(); Person.prototype.sayHi = function() { console.log('hi'); }; friend.sayHi(); // hi
但是如果是重写整个原型对象,那么情况就不一样了。调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象 就相当于切断了构造函数与最初原型之间的联系。 实例中的指针仅指向原型,而不是指向构造函数。
// 构造函数 function Person() {}; var friend = new Person(); Person.prototype = { constructor: Person, sayHi: function() { console.log(hi); } }; friend.sayHi(); // Uncaught TypeError: friend.sayHi is not a function
创建了一个Person的实例,然后又重写了其原型对象。但是在使用sayHi()时发生了错误,这个时候实例所指向的原型对象是一个新的对象。重写原型对象切断了现有原型与之前已经存在的对象实例直接的联系。所以报错了。
5.原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都采用这种模式,所有的原生引用类型(Object、Array、String)等,都在其构造函数的原型上定义了方法。可以像修改自定义对象的原型一样修改原生对象的原型。
二、原型模式的缺点
对于包含引用类型值的属性来说,所有实例在默认的情况下都会取得相同的属性值。
// 构造函数 function Person() {}; // 原型属性prototype Person.prototype = { constructor: Person, friends: ['张三', '李四'], } let person1 = new Person(); let person2 = new Person(); person1.friends.push('王五'); console.log(person1.friends); // ["张三", "李四", "王五"] console.log(person2.friends); // ["张三", "李四", "王五"]
由于friends存在于Person的原型对象中,所以person1对friends的修改也会通过person2反应出来,但是实例对象一般都是要有属于自己的全部属性,正因为如此,很少有人单独使用原型模式来创建对象。