zoukankan      html  css  js  c++  java
  • 深入浅出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学习札记(十五) 高级特性1 切片
    LeetCode Longest Substring Without Repeating Characters
    Python学习札记(十四) Function4 递归函数 & Hanoi Tower
    single number和变体
    tusen 刷题
    实验室网站
    leetcode 76. Minimum Window Substring
    leetcode 4. Median of Two Sorted Arrays
    leetcode 200. Number of Islands 、694 Number of Distinct Islands 、695. Max Area of Island 、130. Surrounded Regions 、434. Number of Islands II(lintcode) 并查集 、178. Graph Valid Tree(lintcode)
    刷题注意事项
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/13847309.html
Copyright © 2011-2022 走看看