1. 理解原型prototype
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
person1.sayName();
let person2 = new Person();
person2.sayName();
console.log(person1.sayName == person2.sayName);
- 我们要理解,构造函数(Person),原型对象(Person.prototype/person1[[Prototype]]),实例(person1/person2)他们之间的关系,这三者是三个不同的对象。
- 构造函数的prototype属性指向原型对象:
Person.prototype = 原型对象
。 - 原型对象的constructor属性引用这个构造函数:
Person.prototype.constructor = Person
。 - 实例的[[Prototype]](通过__proto__来访问)指向原型对象:
person1.__propt__ = Person.prototype
。 - 正常的原型链都会终止于Object的原型对象,Object对象的原型的原型是null。
- instanceof用来检查实例的原型链中是否包含指定构造函数的原型。
2.关于原型的一些方法
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1));
console.log(Person.prototype.isPrototypeOf(person2));
- 用原型对象调用isPrototype来检查实例__proto__是否指向这个原型对象。
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
console.log(Object.getPrototypeOf(person1)== Person.prototype);
console.log(Object.getPrototypeOf(person1).name);
- Object.getPrototypeOf方法来获取实例的原型对象。也可以通过实例的__proto__属性
let biped = {
numLegs:2
};
let person = {
name: 'Matt'
};
Object.setPrototypeOf(person,biped);
console.log(person.name);
console.log(person.numLegs);
console.log(Object.getPrototypeOf(person) === biped);
- Object.setPrototypeOf方法可以向实例的私有属性[[Prototype]]写入一个新值,用来重写一个对象的原型继承关系。该方法会严重影响代码性能。
let biped = {
numLegs: 2
};
let person = Object.create(biped);
person.name = 'Matt';
console.log(person.name);
console.log(person.numLegs);
console.log(Object.getPrototypeOf(person) === biped);
- 为避免使用Object.setPrototpeOf可能造成的性能下降,可以通过Oject.create来创建一个新对象,同时为其指定原型。
2.原型层级
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
person1.name = "Greg";
console.log(person1.name);
console.log(person2.name);
delete person1.name;
console.log(person1.name);
- 通过对象访问属性时,会按照这个属性的名称开始搜索。
(1)如果实例上发现了给定的名称,则返回该名称对应的值。
(2)如果实例上没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值。 - 可以通过实例来读取原型对象上的值,但是不能通过实例来重写这些值。
- 可以给实例添加一个属性,这个属性就会遮蔽原型对象上的同名属性。
- 使用delete操作符删除实例上这个属性,才能取消这个遮蔽。
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
console.log(person1.hasOwnProperty("name"));
person1.name = "Grey";
console.log(person1.hasOwnProperty("name"));
- hasOwnProperty这个方法用于判断元素来自于实例中还是原型中。
(1)来自于实例中则返回true
(2)来自于原型中则返回false - Object.getOwnPropertyDescriptor方法只对实例属性有效。要取得原型属性的描述符,就必须直接在原型对象上调用Object.getOwnPropertyDescriptor。
3.原型和in操作符
function Person(){};
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
console.log("name" in person1);
person1.name = "Grey";
console.log("name" in person1);
- in操作符与hasOwnProperty方法不同,无论该属性是在实例上还是在原型上,该操作符都会返回true。
console.log(!Object.hasOwnProperty("name")&&("name" in person1))
- 可以用上面的方式来确定元素是否存在与于原型上
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let keys = Object.keys(Person.prototype);
console.log(keys);
let p1 = new Person();
p1.name = "Rob";
p1.age = 31;
let p1keys = Object.keys(p1);
console.log(p1keys);
- Object.keys方法接收一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组。
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
let keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);
- Object.getOwnPropertyNames接受一个对象作为参数,返回包含其所有实例属性的数组。
let k1 = Symbol('k1');
let k2 = Symbol('k2');
let o = {
[k1]:'k1',
[k2]:'k2',
name:"aaaaa"
};
console.log(Object.getOwnPropertySymbols(o));
- Object.getOwnPropertyNames接收一个对象作为参数,返回包含其符号属性的数组。
4.属性枚举排序
let k1 = Symbol('k1');
let k2 = Symbol('k2');
let o = {
1:1,
first:'first',
[k1]:'sym2',
second:'second',
0:0
};
o[k2]='sym2';
o[3] = 3;
o.thired = 'thired';
o[2] = 2;
console.log(Object.getOwnPropertyNames(o));
console.log(Object.getOwnPropertySymbols(o));
- 在属性枚举中:
(1)for-in循环和Object.keys的枚举顺序是不确定的,取决于JavaScript引擎,可能因浏览器而异。
(2)Object.getOwnPropertyNames
和Object.getOwnPropertySymbols
和Object.assign
他们的枚举顺序是确定性的。先以升序枚举数值键,然后以插入顺序枚举字符串和符号键