理解javascript的原型链
作为已经有了一年多经验的前端的我,时常还是不能够理解javascript原型链中对象的__proto__和prototype的指向问题,以及一些时候instanceof 检测问题。所以这次我准备深入的理解清楚javascript原型链的来龙去脉,并作出自己的总结。
首先画出我总结的一张原型链图,然后我们一步步印证图中每个指向代表的关系。
1.对象和函数
创建对象的方式有很多种,字面量,构造函数和Object.create(__proto__) (比较特殊,把新对象的__proto__ 指向传入对象)。创建函数的方式也有很多,函数声明,函数表达式,构造函数等。javascript中函数也是一种引用类型的数据类型,所以函数也是对象。
// 函数也是对象:对象分为函数对象和普通对象 // 对象和对象的创建 var o1 = {}; var o2 = new Object(); var o3 = new f1(); var o4 = Object.create({}); // 函数和函数的创建 function f1() {} var f2 = new Function(); var f3 = function() {}; console.log("o1", typeof o1); //object console.log("o2", typeof o2); //object console.log("o3", typeof o3); //object console.log("o4", typeof o4); //object console.log("f1", typeof f1); //function console.log("f2", typeof f2); //function console.log("f3", typeof f3); //function console.log("Function", typeof Function); //function console.log("Object", typeof Object); //function
2.原型链上的指针
原型链是一种链表数据结构,其上的每一个节点都有相应的指针来指向对应的节点。
- 普通对象根本上都是由构造函数创建,
- 普通对象在创建的时候带有constructor 和 __proto__ 两个指针,函数对象在创建的时候带有constructor , __proto__和prototype三个指针。
- 普通对象作为一个实例其constructor指针指向其构造函数,__proto__指向其构造函数的原型
- 函数对象其constructor指向Function函数(所有函数的构造函数),__proto__指向一个匿名函数,prototype指向带有其构造函数属性的一个原型对象(原型对象里面有一个constructor指针指回向函数对象,即函数对象的原型的构造函数是其自己)
// 普通对象都是由构造函数创建,普通对象在创建的时候带有constructor 和 __proto__ 属性 // constructor 是一个指向其构造函数的指针 // __proto__ 是一个指向构造函数原型对象的指针 console.log("o1.constructor", o1.constructor); //ƒ Object() { [native code] } console.log("o2.constructor", o2.constructor); //ƒ Object() { [native code] } console.log("o3.constructor", o3.constructor); //ƒ f1() {} console.log("o1.prototype", o1.prototype); //undefined console.log("o2.prototype", o2.prototype); //undefined console.log("o3.prototype", o3.prototype); //undefined console.log("o1.__proto__", o1.__proto__ === Object.prototype); // true console.log("o2.__proto__", o2.__proto__ === Object.prototype); //true console.log("o3.__proto__", o3.__proto__ === f1.prototype); //true // 构造函数在创建的时候也是带有constructor __proto__ 属性 ,并且比普通对象多一个 prototype 属性 // 构造函数的constructor指针都是指向 Function // 构造函数的__proto__指针都是指向 一个匿名函数 console.log("f1.constructor", f1.constructor); //ƒ Function() { [native code] } console.log("f2.constructor", f2.constructor); //ƒ Function() { [native code] } console.log("f3.constructor", f3.constructor); //ƒ Function() { [native code] } console.log("f1.prototype", f1.prototype ); //{constructor:f f1(),__proto__:Object} console.log("f2.prototype", f2.prototype); //{constructor:f anonymous(),__proto__:Object} console.log("f3.prototype", f3.prototype); //{constructor:f (),__proto__:Object} console.log("f1.__proto__", f1.__proto__); // ƒ () { [native code] } console.log("f2.__proto__", f2.__proto__); //ƒ () { [native code] } console.log("f3.__proto__", f3.__proto__); //ƒ () { [native code] }
3.Function函数和Object函数以及匿名函数的关系
- Function函数最为函数对象其constructor指针指向自己,__proto__指向匿名函数,prototype也指向匿名函数。
- Object函数的constructor指向Function函数,__proto__指向匿名函数,prototype指向其自己的原型对象(Object.prototype)。
- 匿名函数是一种特殊的对象,没有prototype属性,其__proto__指向Object.prototype(和普通对象一样)。
// 所以在Function的原型其实是一个匿名函数 console.log("Function.prototype", Function.prototype); //ƒ () { [native code] } // 那么Function作为构造函数其 constructor 和 __proto__ 分别指向哪里呢? console.log("Function.constructor", Function.constructor); // ƒ Function() { [native code] } console.log("Function.__proto__", Function.__proto__); //ƒ () { [native code] } // 可以看到 Function 的构造函数是自己 ,原型对象是匿名函数 // 那么这个匿名函数有没有 constructor __proto__ prototype 三个属性呢?又分别指向哪里呢 console.log("Function.__proto__.constructor", Function.__proto__.constructor); // ƒ Function() { [native code] } console.log("Function.__proto__.prototype", Function.__proto__.prototype); // undefined console.log("Function.__proto__.__proto__", Function.__proto__.__proto__===Object.prototype); // true // Function.__proto__ 是一个匿名函数 ,其是一个特殊的对象,没有prototype属性, // 和普通对象一样只有 constructor 和 __proto__ 指针 // 我们可以得出这个特殊的匿名函数的__proto__指向是对象的原型 // 这个对象的原型是否还存在指针,其指向是哪里呢? console.log("Object.prototype.constructor",Object.prototype.constructor); // ƒ Object() { [native code] } console.log("Object.prototype.__proto__",Object.prototype.__proto__); // null console.log("Object.prototype.prototype",Object.prototype.prototype); // undefined
Object.create()方法对原型链的作用
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
console.log("o4.__proto__", o4.__proto__); // {} console.log("o4.prototype", o4.prototype); // undefined console.log("o4.constructor", o4.constructor); // ƒ Object() { [native code] }
instanceof的理解
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
返回objectA.__proto__=== ...objectX.__proto__... === objectB.prototype
// objectA instaceof objectB : instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。 // return objectA.__proto__ === objectB.prototype console.log("o1 instanceof Object",o1 instanceof Object) // true console.log("o1 instanceof Function",o1 instanceof Function) // false console.log("f1 instanceof Object",o1 instanceof Object) // true console.log("f1 instanceof Function",o1 instanceof Function) // false console.log("Object instanceof Function",Object instanceof Function) // true console.log("Function instanceof Object",Function instanceof Object) // true
需要注意的是:objectA.__proto__ === objectB.prototype 并不是永远成立,当objectB的prototype的指向改变的时候,则不成立。
// 需要注意的是:objectA.__proto__ === objectB.prototype并不是永远成立,当objectB的prototype的指向改变的时候,则不成立。 function C() {} var c = new C(); var d = {}; console.log("c instanceof C", c instanceof C); C.prototype = d; console.log("c instanceof C", c instanceof C); // console.log("c instanceof d",c instanceof d) // Uncaught TypeError: Right-hand side of 'instanceof' is not callable var e = new C(); console.log("e.__proto__", e.__proto__); // {}
参考:
《javascript高级程序设计》