prototype属性
- JS实现继承的设计思想是,原型对象的所有属性和方法都能被实例对象共享。
prototype是函数的一个属性,对于普通函数来说基本无用。但是对于构造函数来说,生成实例时,该属性会自动成为实例对象的原型对象。
function Person(){
}
Person.prototype.lastName = 'zhangsan';
var person = new Person();
var person1 = new Person();
console.log(person.lastName); // 'zhangsan' person对象没有lastName属性,所以会向原型对象上查找
console.log(person1.lastName); // 'zhangsan'
- 把那些不变的、共有的属性和方法提取出来,直接定义到 prototype上,这样所有对象实例可以共享这些属性和方法。
function Person(name){
this.name = name;
}
//共有属性
Person.prototype.sex = '男';
//共有方法
Person.prototype.say = function(){
console.log('我会说话');
}
var person = new Person('peter');
var person1 = new Person('bob ');
console.log(person.sex); // '男'
console.log(person1.sex); // '男'
console.log(person.say()); // '我会说话'
console.log(person1.say()); // '我会说话'
如果把同一类对象相同的属性和方法都定义为构造函数的实例成员,则每次实例化对象时都会在内存中新开辟空间,造成内存浪费。
__proto__属性
- 每个对象都有一个
__proto__属性指向它的原型对象(构造函数的prototype),该属性可读写。 - 此属性本质上是一个内部属性,只有浏览器才有这个属性。
不建议使用,而是用getPrototypeOf和setPrototypeOf替代。
var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true
原型链
- 所有对象都有自己的原型对象
prototype,任何一个对象都可以充当其他对象的原型。由于原型对象也是对象,所以它也有自己的原型,因此就会形成一个原型链。 - 读取对象的某个属性时,JS会首先寻找对象自身的属性,如果找不到,就去它的原型去找,如果还是找不到,就去原型的原型去找,直到找到
Object.prototype还是找不到,则返回undefined。 - 如果构造函数中实例成员覆盖了原型上的属性或方法,则以实例成员的结果为准(就近原则)。
function Father(name, hobbit) {
this.name = name;
this.hobbit = hobbit;
}
Father.prototype.sex = '男';
var father = new Father('james', 'smoke');
function Son(name) {
this.name = name;
}
Son.prototype = father; // father对象实例指向了Son的原型
var son = new Son('peter');
console.log(son.name); // 'peter'
// son对象中有 name属性。停止查找
console.log(son.hobbit); // 'smoke'
// son对象中没有 hobbit属性,沿着__proto__向上查找:
// 1. son的__proto__指向 Son.prototype
// 2. Son.prototype指向 father对象
// 3. father对象中有 hobbit属性,停止查找
console.log(son.sex); // '男'
// son对象中没有 sex属性,沿着__proto__向上查找:
// 1. son的__proto__指向 Son.prototype
// 2. Son.prototype指向 father对象
// 3. father对象中没有 sex属性,沿着__proto__继续向上查找
// 4. father的__proto__指向 Father.prototype
// 5. Father.prototype中有 sex属性,停止查找
console.log(son.age); // undefined
// son对象中没有 age属性,沿着__proto__向上查找:
// 1. son的__proto__指向 Son.prototype
// 2. Son.prototype指向 father对象
// 3. father对象中没有 age属性,沿着__proto__继续向上查找
// 4. father的__proto__指向 Father.prototype
// 5. Father.prototype中没有 age属性,沿着__proto__继续向上查找
// 6. Father.prototype中__proto__指向 Object.prototype
// 7. Object.prototype中没有__proto__对象,表示已经到达了原型链的终点,依然没找到,返回 null
// 8. 最终结果,返回 undefined
Function 和 Object
- 所有函数都是
Function构造函数创建的实例对象。 Object函数本身也是Function的实例对象,但Object.prototype是Object构造函数的实例对象。
function Test(){}
var test = new Test(); // test对象是由 Test构造方法构造出来的
console.log(test.__proto__ === Test.prototype); // true
// var Test = new Function(...); // function Test(){}是由 Function构造方法构造出来的
console.log(Test.__proto__ === Function.prototype); // true __proto__指向构造函数的原型
console.log(Function.__proto__ === Function.prototype); // true 底层规定
const obj = {}; // 等价于 const obj = new Object();
console.log(typeof Object); // "function" Object 又是由 Function 构造出来的
console.log(Object.__proto__ === Function.prototype); // true __proto__指向构造函数的原型
console.log(Object.__proto__ == Function.__proto__); // true
图解原型链
constructor
- constructor是
prototype对象的一个属性,默认指向prototype对象所在的构造函数。由于constructor属性定义在prototype对象上,所以此属性可以被所有实例对象继承。
function P(){}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true constructor属性被实例对象继承
constructor属性的作用是,可以得知某个实例对象到底是哪一个构造函数产生的。
constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,需要同时修改constructor属性,防止引用时候报错。
function F(){}
function F2(){}
console.log(F.prototype.constructor); // F(){}
// 将构造函数F的原型对象修改为F2的实例对象
F.prototype = new F2()
// 通过F构造函数创建一个实例对象
var f = new F();
console.log(f.constructor) // F2(){} 指向错误 constructor指向了F2,表示f实例对象是F2构造函数产生的
// 修改constoructor指向
F.prototype.constructor = F;
console.log(f.constructor) // F(){} 正确
instanceof 运算符
A instanceOf B,返回一个boolean值,表示对象是否为某个构造函数的实例。原理是检查A对象的原型链上能否找到B构造函数的prototype。
function Person(){}
var p = new Person()
p instanceof Person // true p.__proto__ === Person.prototype
p instanceof Object // true p.__proto__ => Person.prototype.__proto__ === Object.prototype
// 对于undefined 和 null总是返回 false
undefined instanceof Object // false
null instanceof Object // false
原型相关方法
Object.getPrototypeOf()
- 返回参数对象的
原型。
function F(){}
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true
Object.getPrototypeOf(F) === Function.prototype // true
Object.getPrototypeOf({}) === Object.prototype // true
Object.getPrototypeOf(Object.prototype) === null // true
Object.setPrototypeOf()
- 为参数对象设置
原型,返回该参数对象。此方法接受两个参数,参数一为要设置原型的对象,参数二为原型对象。
var a = {}
var b = {x: 1}
Object.setPrototypeOf(a, b); // 将 a 对象的原型对象设置为 b
a.x // 1 a 可以使用 b 的属性
Object.getPrototypeOf(a) === b // true
Object.create()
- 接收两个参数,
参数一接收一个对象作为参数,然后以它为原型,返回一个实例对象。参数一不能为空或者不是对象,否则就会报错。
var a = {
print: function () {
console.log('hello');
}
};
var b = Object.create(a);
Object.getPrototypeOf(b) === a // true
b.print() // 'hello'
Object.create() // Uncaught TypeError: Object prototype may only be an Object or null
Object.create(123) // Uncaught TypeError: Object prototype may only be an Object or null
- 如果想要生成一个
不继承任何属性的对象,可以将参数设置为null。
var obj = Object.create(null);
Object.getPrototypeOf(obj); // null
参数二是一个属性描述对象,它所描述的对象属性会添加到实例对象上。
var obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});
obj // {p1: 123, p2: 'abc'}
Object.prototype.isPrototypeOf()
- 判断该对象是否为参数对象的原型,只要该对象处在参数对象的原型链上,都返回
true。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o1.isPrototypeOf(o2); // true o2是以o1为原型创建的实例对象,所以o1是o2的原型
o1.isPrototypeOf(o3); // true o1和o2都是o3对象的原型
Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
// Object.prototype处于原型链的最顶端,所以对各种实例对象都返回true,除了继承自null的实例对象