js

JS原型与原型链

2020-04-02  本文已影响0人  书虫和泰迪熊

prototype(原型对象)

prototype 是函数才有的属性,只要创建一个函数,就会为该函数创建一个prototype 属性(称为原型对象)
每个原型对象(prototype) 都有两个属性:

  1. constructor 该属性指向构造函数
    Person.prototype.constructor === Person
    
  2. __ proto__
    image.png

__ proto__ (原型)

每创建一个JavaScript 对象(除了null),该对象上都会有一个 __ proto__ 属性,这个属性指向该函数的原型对象

image.png
总结公式如下
var 对象 = new 函数()
对象.__proto__  === 函数.prototype

简单描述 new 一个对象过程
正常 new一个

image.png

自己实现一个


image.png

原型链

上面已经说了

p.__proto__  ===  Person.prototype   // true

同样 Person.prototype 也是一个对象,所以他也有 __ proto__ 属性,所以

Person.prototype.__proto__ === Object.prototype  // true

Object也有__ proto__ 属性,但是比较特殊是null,说明 Object 没有继承

Object.Prototype.__proto__ === null  // true

所以有

p.__proto__.__proto__ === Object.prototype   // true
p.__proto__.__proto__.__proto__ === null      // true

我们把这种由 __ proto__ 串起来直到 Object.prototype.__ proto__ 为 null 的链叫原型链,原型链的形成真正靠的是 __ proto__,而不是 prototype。
如下图


image.png

由此可以得到下面结论
所有函数都是 Function 的实例,Object, Number, String 这些都是函数 - 构造函数,所以他们都是Function 的实例。

Number.__proto__ === Function.prototype  // true
String.__proto__ === Function.prototype     // true
Object.__proto__ === Function.prototype    // true

那Function是谁的实例?
Function的直接实例还是Function

Function.__proto__ === Function.prototype   // true
Function.__proto__.__proto__ === Function.prototype.__proto__  // true



instanceof的底层实现原理,手动实现一个instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
即 实例.__ proto __ ==? 函数.prototype 或者沿着原型链想上(Object)寻找,如下
实例.__ proto __ . __ proto __ ....... ==? 函数.prototype

// 因为 instanceof 是个关键字,我们只能写一个函数 instance_of 来模拟 instanceof 功能
function instance_of(L, R) {   //L 表示左表达式,R 表示右表达式
    var O = R.prototype;
    L = L.__proto__;
    while (true) { 
        if (L === null) 
        return false; 
        if (O === L) // 这里重点:当 O 严格等于 L 时,返回true 
        return true; 
        L = L.__proto__; 
    } 
}

// 测试
var a = []
var b = {}
 
function Foo(){}
var c = new Foo()
 
function child(){}
function father(){}
child.prototype = new father() 
var d = new child()
 
console.log(instance_of(a, Array)) // true
console.log(instance_of(b, Object)) // true
console.log(instance_of(b, Array)) // false
console.log(instance_of(a, Object)) // true
console.log(instance_of(c, Foo)) // true
console.log(instance_of(d, child)) // true

问题:A instanceof B 和 B.isPrototypeOf(A) 区别
· A instanceof B :instanceOf运算符用于检测 构造函数(B)的prototype属性是否出现在某个实例对象(A)的原型链上。
· B.isPrototypeOf(A):检测B对象是否出现于A对象的原型链上
注:isPrototypeOf()instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身。
若使用 isPrototypeOf 检测出来的结果为 true 则 instanceof 一定为true,反之不成立。

小练1:

function Person() {
}

Person.prototype.name = 'Kevin';

var person = new Person();

person.name = 'Daisy';
console.log(person.name) // Daisy

delete person.name;
console.log(person.name) // Kevin

第一次输出 person.name 自然是 Daisy,但是当我们删除了 person.name 之后,在person对象中读取不到 name 属性,就会从person的原型 person.__ proto__ 中读取,即从 Person.prototype 中读取name。则为 Kevin。

继承

原型链是实现继承的一种方式
String, Number 继承 Object

String.prototype.__proto__ === Object.prototype    // true
Number.prototype.__proto__ === Object.prototype  // true

Object 没有继承

Object.prototype.__proto__ === null

Function继承谁呢?哈哈!!Function.prototype 也是一个对象,所以Function继承Object~~

Function.prototype.__proto__ === Object.prototype

小练2:

var F = function() {}
Object.prototype.a = function() {}
Function.prototype.b = function() {}
var f = new F()

问:f.a 和 f.b 那个能拿到???
答:f.a 能拿到,f.b 不能拿到
解析:f.a 先去 f 对象中查找 a 发现没有,再去 f.__ proto__中查找 a,即是F.prototype 中查找 a,又没有,F.prototype是对象再接着去 F.prototype的原型,即F.prototype.__ proto__ 上查找,即Object.prototype 上查找,lucky 找到

补充

constructor

function Person() {
}
var person = new Person();
console.log(person.constructor === Person); // true

当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:

person.constructor === Person.prototype.constructor

__ proto__
其次是 __ proto__ ,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.__proto __ 时,可以理解成返回了 Object.getPrototypeOf(obj)。

真的是继承吗?
最后是关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:
继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些

上一篇 下一篇

猜你喜欢

热点阅读