• JavaScript原型继承工作原理


    原型继承的定义

    当你阅读关于JS原型继承的解释时,你时常会看到以下这段文字:

    当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。——出自JavaScript秘密花园

    大多数JavaScript的实现用 __proto__ 属性来表示一个对象的原型链。在这篇文章里我们将看到__proto__与 prototype 的区别何在。

    注:__proto__ 是一个不应在你代码中出现的非正规的用法,这里仅仅用它来解释JavaScript原型继承的工作原理。

    以下代码展示了JS引擎如何查找属性:

    function getProperty(obj, prop) {
        if (obj.hasOwnProperty(prop))
            return obj[prop]
    
        else if (obj.__proto__ !== null)
            return getProperty(obj.__proto__, prop)
    
        else
            return undefined
    }

    让我们举一个常见的例子:二维点,拥有二维坐标 x y ,同似拥有一个 print 方法。

    用之前我们说过的原型继承的定义,我们创建一个对象 Point ,拥有三个属性:xy 和 print。为了能创建一个新的二维点,我们需要创建一个新的对象,让他其中的 __proto__ 属性指向Point :

    var Point = {
        x: 0,
        y: 0,
        print: function () { console.log(this.x, this.y); }
    };
    
    var p = {x: 10, y: 20, __proto__: Point};
    p.print(); // 10 20

    JavaScript怪异的原型继承

    令人困惑的是,每个教授原型继承的人都不会给出上面那样的代码,反而会给出下面这样的代码:

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    Point.prototype = {
        print: function () { console.log(this.x, this.y); }
    };
    
    var p = new Point(10, 20);
    p.print(); // 10 20
    

      

    这和说好的不一样啊,这里 Point 变成了函数,然后还有个什么 prototype 的属性,而且有了new 运算符。这他喵的是什么情况?

    new 运算符是如何工作的

    造物者 Brendan Eich 想让JS和传统的面向对象的编程语言差不太多,如Java和C++。在这些语言里,我们采用 new 运算符来给类实例化一个新的对象。所以他在JS里写了一个 new 运算符。

    • C++里有用来初始化实例属性的构造函数概念,因此 new 运算符必须针对函数。
    • 我们需要将对象的方法放到一个地方去,既然我们在用原型语言,我们就把它放到函数的原型属性中去。

    new 运算符接受一个函数 F 及其参数:new F(arguments...)。这一过程分为三步:

    1. 创建类的实例。这步是把一个空的对象的 __proto__ 属性设置为 F.prototype 。
    2. 初始化实例。函数 F 被传入参数并调用,关键字 this 被设定为该实例。
    3. 返回实例。

    现在我们知道了 new 是怎么工作的,我们可以用JS代码实现一下:

    function New (f) {
        var n = { '__proto__': f.prototype }; /*第一步*/
        return function () {
            f.apply(n, arguments);            /*第二步*/
            return n;                         /*第三步*/
        };
    }

    一个小小的例子来看一下他的工作状况:

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    Point.prototype = {
        print: function () { console.log(this.x, this.y); }
    };
    
    var p1 = new Point(10, 20);
    p1.print(); // 10 20
    console.log(p1 instanceof Point); // true
    
    var p2 = New (Point)(10, 20);
    p2.print(); // 10 20
    console.log(p2 instanceof Point); // true
    

      

    JavaScript中真正的原型继承

    JS的ECMA规范只允许我们采用 new 运算符来进行原型继承。但是大宗师 Douglas Crockford 却发现了一种可以利用 new 来实现真正的原型继承的方式!他写下了 Object.create 函数如下:

    Object.create = function (parent) {
        function F() {}
        F.prototype = parent;
        return new F();
    };

    这看起来蛮奇怪的,但却是相当的简洁:它创建了新的对象,并将其原型设置为你想设置的任意值。如果我们允许使用 __proto__ ,那我们也可以这样写:

    Object.create = function (parent) {
        return { '__proto__': parent };
    };

    下面这段代码就是让我们的 Point 采用真正的原型继承:

    var Point = {
        x: 0,
        y: 0,
        print: function () { console.log(this.x, this.y); }
    };
    
    var p = Object.create(Point);
    p.x = 10;
    p.y = 20;
    p.print(); // 10 20
    

      

  • 相关阅读:
    ContextLoaderListener作用详解
    @Autowired与@Resource的区别
    使用SimpleDateFormat时的日期和时间模式
    git将本地内容传送到远程仓库出现![rejected] master -> master (fetch first)错误
    使用SSH框架遇到的错误总结
    struts2 action中字符串转json对象出错 java.lang.NoClassDefFoundError: org/apache/commons/lang/exception/NestableRuntimeException
    2017百度web前端实习生在线笔试题
    学习模型-视图-控制器MVC模式
    DOM元素加载之前执行的jQuery代码
    使用$.post和action或servlet交互 URL出现 http://localhost:8080/../[object%20Object] 错误的问题解决
  • 原文地址:https://www.cnblogs.com/sandianbaozi/p/3628422.html
Copyright © 2020-2023  润新知