JavaScript 原型链探索

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39759115/article/details/88420556

JavaScript 原型链探索

很早就知道了 JavaScript 的原型与原型链,但是关于原型链之间的关系却一直没有仔细研究,趁着有空探索一番。

对象的原型

每个对象都有一个隐藏的属性——__proto__(隐式原型),这个属性引用了创建这个对象的函数的 prototype

var obj = {a: 10};

console.log(obj.prototype);                         // undefined  只有函数对象才有
console.log(obj.__proto__ === Object.prototype);    // true 
console.log(Object.prototype.__proto__ === null);   // true  

__proto__ 是 JS 的非标准但许多浏览器实现的属性,即[[prototype]],也就是 Object.[[Prototype]] === Object.__proto__

函数的原型

所有 javascript 对象都有一个指向它的原型对象的内部链接 [[prototype]],但只有函数对象才有 prototype 这个属性。

prototype 的属性值是一个对象,默认的只有一个叫做 constructor 的属性,指向这个函数本身。

你可以在函数的 prototype 中新增自己的属性:

    function Person() {};
    Person.prototype.myType = 'Humanity';
    
    var girl = new Person;      // girl.__proto__ = Person.prototype
    console.log(girl.myType);   // 'Humanity'

函数也是对象,所以也具有 __proto__ 属性:

    console.log(Person.__proto__ === Function.prototype);     // true

原型链

我们知道,实例对象的 constructor 属性指向其构造函数;实例对象的 __proto__ 指向其构造函数的 prototype 属性。

那么一个对象的原型链可以通过 __proto__ 属性来得知:

    var myObj = new Object();
    console.log(myObj.__proto__ === Object.prototype);  // true
    console.log(Object.prototype.__proto__ === null);   // true

再举一个例子:

    function Fn(){};
    var myObj = new Fn;
    
    console.log(myObj.__proto__ === Fn.prototype);              // true
    console.log(Fn.prototype.__proto__ === Object.prototype);   // true
    console.log(Object.prototype.__proto__ === null);           // true

上面都是对象的原型链,接下来看一下函数的:

    function myFun() {}
    
    console.log(myFun.__proto__ === Function.prototype);                // true
    console.log(Function.prototype.__proto__ === Object.prototype);     // true
    console.log(Object.prototype.__proto__ === null);                   // true

简单总结一下上述几个例子,原型链可以说成是实例对象(函数) a__proto__a.__proto__.__proto__ 这样层层递进,最后到原型的尽头 null ,所经过的原型对象组成的链条。

null 表示"没有对象",即该处不应该有值。

知道了原型链,接下来我们通过一个让人惊讶的例子来探索他们之间的关系:

console.log(Object.constructor === Function);           // ture
console.log(Object.__proto__ === Function.prototype);   // true

console.log(Array.__proto__ === Function.prototype);        // true
console.log(String.__proto__ === Function.prototype);       // true
console.log(Number.__proto__ === Function.prototype);       // true
console.log(Boolean.__proto__ === Function.prototype);      // true
console.log(RegExp.__proto__ === Function.prototype);       // true

通过上面代码我们可以得知:构造函数 Object() 是通过构造函数 Function() 实例化出来的。甚至所有的构造函数都是构造函数 Function() 实例化出来的。

那么构造函数 Function() 是从和而来呢?构造函数 Function() 也是通过构造函数 Function() 实例化出来的。
看代码:

console.log(Function.constructor === Function);             // ture
console.log(Function.__proto__ === Function.prototype);     // true

function myfn() {};
console.log(myfn.__proto__ === Function.prototype);  // true

所以,Function 是被自身创建的,它的 __proto__ 指向自身的 prototype

我们知道了构造函数 Function 是所有构造器的起点,那他们的原型关系呢?

看代码:

console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype);    // true
console.log(String.prototype.__proto__ === Object.prototype);   // true
console.log(Number.prototype.__proto__ === Object.prototype);   // true
console.log(Boolean.prototype.__proto__ === Object.prototype);  // true
console.log(RegExp.prototype.__proto__ === Object.prototype);   // true
console.log(Object.prototype.__proto__);                        // null

可以得知:所有构造函数(除了 Object)的原型对象,都是由构造函数 Object 创建的,所以都指向 Object.prototype。最后 Object.prototype__proto__ 指向 null

总结一下原型关系图:
原型关系图

简单总结一下:

  1. 原型链的终点为 null

  2. 所有构造函数都是构造函数 Function 的实例对象,包括它自己。所以,所有构造函数的 __proto__ 都指向 Function.prototype

所有的对象和函数通过隐藏的 __proto__ 属性的关联形成原型链结构。

判断原型

在 JavaScript 中,typeof 用来判断基本类型,instanceof 用来判断引用类型。

A instanceof B 运算符的判断队则是:如果右侧构造函数的 prototype 属性能在左侧的对象的原型链中找到,那么就返回 true,否则就返回 false。

通过上以规则,你可以解释很多比较怪异的现象,例如:

console.log(Object instanceof Function);  // true
console.log(Function instanceof Object);  // true
console.log(Function instanceof Function);  // true

instanceof 表示的就是一种继承关系,或者原型链的结构。

JavaScript 中的继承是通过原型链来实现的。例如:

function Fn() {};
var f1 = new Fn();

f1.a = 10;

Fn.prototype.a = 100;
Fn.prototype.b = 200;

console.log(f1.a);  // 10
console.log(f1.b);  // 200

上面代码中,f1 是构造 Fn 函数 new 出来的对象,f1.af1 对象的基本属性,f1.b 是怎么来的呢?

Fn.prototype 得来,因为 f1.__proto__ 指向的是 Fn.prototype

可以通过 hasOwnProperty() 区分一个属性到底是自己的还是从原型中继承的:

for(item in f1){
    console.log(item); // a  b
    if(fi.hasOwnProperty(item)) console.log(item);  // a
}

f1 的这个 hasOwnProperty 方法从何而来? 它是从 Object.prototype 中继承来的。打印 console.log(Object.prototype) 结果如下:

constructor:ƒ Object()
hasOwnProperty:ƒ hasOwnProperty()
isPrototypeOf:ƒ isPrototypeOf()
propertyIsEnumerable:ƒ propertyIsEnumerable()
toLocaleString:ƒ toLocaleString()
toString:ƒ toString()
valueOf:ƒ valueOf()
__defineGetter__:ƒ __defineGetter__()
__defineSetter__:ƒ __defineSetter__()
__lookupGetter__:ƒ __lookupGetter__()
__lookupSetter__:ƒ __lookupSetter__()
get __proto__:ƒ __proto__()
set __proto__:ƒ __proto__()

对象的原型链是沿着 __proto__ 这条线走的,因此在查找 f1.hasOwnProperty 属性时,就会顺着原型链一直查找到 Object.prototype

由于所有的对象的原型链都会找到 Object.prototype,因此所有的对象都会有 Object.prototype 方法。这就是所谓的“继承”。

学习资料

http://www.ecma-international.org/ecma-262/8.0/index.html

http://lzw.me/pages/ecmascript/

深入理解javascript原型和闭包(5)——instanceof

Javascript 花园:http://bonsaiden.github.io/JavaScript-Garden/zh/

Javascript 继承机制的设计思想:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

法国程序员 Vjeux 解释原型链:http://blog.vjeux.com/2011/javascript/how-prototypal-inheritance-really-works.html

展开阅读全文

没有更多推荐了,返回首页