• 深入浅出JS原型链


    定义

    我们先来看看它的定义

    当js在一个对象上查找属性的时候,首先查找对象本身的属性(即声明时候定义的属性),如果在这些属性中没有找到目标属性,那么js会找它的__proto__对象,如果这个对象没有,那么继续找__proto__对象的__proto__对象,直到__proto__为null或者找到目标属性为止...... 这个__proto__的关系链,就是我们说的原型链

    特性

    其实原型链真的很简单,你只要记住下面这三个点就完了。

    • 每一个对象都有__proto__属性
    • 每一个函数都有prototype属性,这个属性值也是一个对象
    • 每一个对象的__protp__都会指向它的构造函数的prototype

    实践

    俗话说实践出真知,我们一步步来验证这几个特性.

    验证第一点

    大家可以分段复制我的代码粘贴到浏览器控制台中

    let obj = {name:"leelei"};
    console.log(obj.__proto__);
    
    let arr = [1,2,3]
    console.log(arr.__proto__);
    
    let str = "http://www.leelei.info";
    console.log(str.__proto__);
    
    let num = 100;
    console.log(num.__proto__);

    当你全部打印完以后,你会发现一个问题,诶,我的str和num不是基础类型吗?不是对象也有proto属性?

    这里我们要提一下js的隐式转化规则了,当你访问基础类型的一些属性或者调用一些方法的时候,你的基础类型会被js给包装起来,就像下面这样

    str.__proto__ => new String(str).__proto__;
    num.__proto__ => new Number(num).__proto__;

    那么这个new String和new Number哪里来的噶?

    它们都是js原生的构造函数,总共有以下几种

    • Object
    • Number
    • String
    • Boolean
    • RegExp
    • Error
    • Function
    • Array
    • Date
    我们现在已经验证了第一点了,每个对象(基础类型是通过原生构造函数转成了对象)都有__proto__属性

    验证第二点

    请一行一行复制以下代码到控制台中

    console.log(Object.prototype);
    console.log(Number.prototype);
    console.log(String.prototype);
    console.log(Boolean.prototype);
    console.log(RegExp.prototype);
    console.log(Error.prototype);
    console.log(Function.prototype);
    console.log(Array.prototype);
    console.log(Date.prototype);

    如果我们不是构造函数呢

    function fn(){}
    console.log(fn.prototype);

    可以看到,无论是构造函数,还是我们声明的函数都有prototype的属性。
    区别只是原生的构造函数他们的prototype对象上已经自带了一些方法和属性。

    那么综上我们可以验证第二个特性,每一个函数都有prototype属性

    验证第三点

    我们只要判断下第一步的__proto__是否指向第二步对应构造函数的prototype即可。

    let obj = {name:"leelei"};
    obj.__proto__ === Object.prototype; //true
    
    let arr = [1,2,3]
    arr.__proto__ === Array.prototype; //true
    
    let str = "http://www.leelei.info";
    str.__proto__ === String.prototype; //true
    
    let num = 100;
    num.__proto__ === Number.prototype; //true
    
    结果已经显而易见了,第三个论点也可得到论证

    广州设计公司https://www.houdianzi.com 我的007办公资源网站https://www.wode007.com

    从定义出发

    我们根据定义来感受一下这个链条

    let obj = {name:"leelei"};
    
    console.log(obj.__proto.valueOf); //有这个方法嗷
    
    obj.valueOf(); //{name:"leelei"};

    我们可以看到,在我们声明obj的时候并没有这个属性方法,但是我们却可以调用,我们打印了obj的__proto__对象,发现这里存在这个方法,所以可以调用。

    这里就是定义所讲的当对象本身没有这个属性的时候,我们会去它的__proto__对象上找

    这里才只有一层噶?如果隔多几层会不会调用不了了阿?
    我们可以编写一个构造函数,然后给它的原型对象prototype添加自定义方法来试验。

    function Foo(age){this.age = age};
    Foo.prototype.say = function(){console.log('hello')};
    
    let foo = new Foo(18);

    根据特性2,对象的__proto__对象指向构造函数的prototype,所以这样是没什么问题的

    foo.say()
    => foo.__proto__.say()  //hello
    相当于=> Foo.prototype.say()  //hello

    那我们还能不能使用Object上面的方法呢?当然可以

    foo.valueOf()
    => foo.__proto__.valueOf 
    相当于=> Foo.prototype.valueOf 怎么没有嗷?
    
    //第一层找不到喔,那就往上一层
    //不要忘了prototype也是对象!
    //所以它也有__proto__属性
    
    ==> Foo.prototype.__proto__.valueOf 
    相当于==> Object.prototype.valueOf 找到啦

    你可能会有点好奇为什么这两个会等于?
    Foo.prototype.__proto__ === Object.prototype

    因为Foo.prototype是对象,Foo是函数

    //我们可以很显然直到Foo的构造函数是Function
    Foo.__proto__ === Function.prototype;
    
    //但是Foo.prototype的构造函数是谁?
    
    Foo.prototype.__proto__ === 它的构造函数.prototype;

    我们这里需要了解一个constructor的属性,
    如果你在前面的步骤打印过自己定义的函数的prototype值,你会发现里面自带了一个constructor的属性,当你打印constructor的时候会返回这个函数本身,如下所示

    let fn = function(){console.log('hello')};
    console.log(fn.prototype) //{constructor: ƒ}
    fn.prototype.constructor === fn //true

    那么这个东西有什么用呢?
    就是为了告诉我们构造函数是谁。
    在上面我们说到

    Foo.prototype.__proto__=== 它的构造函数.prototype;

    那么显然这样也是相等的

    Foo.prototype.__proto__.constructor === 它的构造函数.prototype.constructor

    又因为

    它的构造函数.prototype.constructor === 它的构造函数

    可以推出

    Foo.prototype.__proto__.constructor === 它的构造函数
    console.log(Foo.prototype.__proto__.constructor)
    //ƒ Object() { [native code] }

    所以我们可以求得Foo.prototype的构造函数是Object

    一点需要注意的

    typeof (Function.prototype); //function
    

    这个居然不是对象?!惊了
    根据特性2,每个函数都有一个prototype属性。
    既然Function.prototype不是对象而是方法,那么它也有prototype属性才对

    Function.prototype.prototype //undefined

    惊了!它居然没有prototype属性!

    莫慌,除这个之外,都是适用的。记住这个特例即可。

  • 相关阅读:
    python获取DBLP数据集
    GNUPLOT 画多组柱状图 以及 折线图 以及各种问题的解决方案
    Leetcode 1:two sum
    测试面试之如何测试一个杯子
    C++小总结
    统计‘1’的个数
    C语言小总结
    剑指offer面试题1---赋值运算符函数
    黑盒测试与白盒测试
    软件测试的原则
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/13847309.html
Copyright © 2020-2023  润新知