摘要:操作符构造步骤有三步构造一个类的实例这个实例是一个空对象,并且他的属性指向构造函数的原型。不优化原生的或自定义的作为构造函数是及其不高效的。
原文地址:Javascript – How Prototypal Inheritance really works
在网上可以看到各种关于Javascript原型继承的文章,但Javascript规范中只提供了new操作符这一种实现原型继承的方法。因此网上大多数的文章是具有迷惑性的,很混乱。这篇文章会让你清晰的认识到什么是真正的原型继承?并且怎么样使用它?
原型继承的定义:你会经常看到如下关于原型继承的定义:
访问一个对象属性的时候,Javascript会沿着原型链向上寻找,直到找到该属性。
Javascript中大多数实现方式都是使用__proto__来指定原型链中下一个被访问的对象,接下来会揭示__proto__与prototype之间的区别。
注意:不要在你的代码中使用__proto__,文中使用它仅仅是为了更好的解释Javascript继承是如何工作的。
下面的代码展示了Javascript引擎如何检索对象的属性(伪代码,仅为了方便理解)
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方法。
用书面语言来表示该定义就是:我们定义了一个有三个属性的对象:x,y和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奇怪的原型继承:
怪异之处在于解释原型继承的人给出的例子往往与他们的定义不相符合,他们给出的代码通常如下所示:
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 想让javascript像Java,C++这些传统的面向对象语言一样,用new操作类直接构造一个实例,所以他给Javascript也添了new操作符。
C++中有构造函数,用来初始化实例的属性。因此,new操作符操作的对象必须是函数。
我们需要把对象的方法挂载到某个地方,由于我们使用的是原型语言,我们把他放在函数的原型属性里。
new操作符构造步骤有三步:构造一个类的实例:这个实例是一个空对象,并且他的__proto__属性指向构造函数的原型。
初始化实例:构造函数被调用,并将this指向这个实例。
返回实例对象。
现在我们了解了new构造的过程,我们在Javascript中实现它:
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); // trueJavascript中真正的原型继承:
Javascript规范只定义了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结论:
我们了解了原型继承并用一种具体的方法实现了它。但这样写有一些缺点:
不标准:__proto__不是标准并不赞成使用, 并且原生的 Object.create 和 Douglas Crockford的实现不等同。
不优化:Object.create (原生的或自定义的)作为构造函数是及其不高效的。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78942.html
摘要:然而,对象字面量不是真正意义上的哈希映射,如果使用不当可能会构成潜在的隐患。空对象创建一个真正的哈希映射的秘诀就是避免原型,及其带来的包袱。在此之前,甚至之后,你应该使用空对象满足你所有的基本哈希映射需求。 在JavaScript中存储键值对的一个简单常见的方法是使用对象字面量。然而,对象字面量不是真正意义上的哈希映射,如果使用不当可能会构成潜在的隐患。虽然目前JavaScrip...
摘要:使用构造函数的原型继承相比使用原型的原型继承更加复杂,我们先看看使用原型的原型继承上面的代码很容易理解。相反的,使用构造函数的原型继承像下面这样当然,构造函数的方式更简单。 五天之前我写了一个关于ES6标准中Class的文章。在里面我介绍了如何用现有的Javascript来模拟类并且介绍了ES6中类的用法,其实它只是一个语法糖。感谢Om Shakar以及Javascript Room中...
摘要:避免脆弱的基类问题。红牌警告没有提到上述任何问题。单向数据流意味着模型是单一的事实来源。单向数据流是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。原文地址 1. 你能说出两种对 JavaScript 应用开发者而言的编程范式吗? 希望听到: 2. 什么是函数编程? 希望听到: 3. 类继承和原型继承的不同? 希望听到 4. 函数式编程和面向对象编程的优缺点? ...
摘要:忍者级别的函数操作对于什么是匿名函数,这里就不做过多介绍了。我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。 JS 中的递归 递归, 递归基础, 斐波那契数列, 使用递归方式深拷贝, 自定义事件添加 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果...
摘要:函数式编程前端掘金引言面向对象编程一直以来都是中的主导范式。函数式编程是一种强调减少对程序外部状态产生改变的方式。 JavaScript 函数式编程 - 前端 - 掘金引言 面向对象编程一直以来都是JavaScript中的主导范式。JavaScript作为一门多范式编程语言,然而,近几年,函数式编程越来越多得受到开发者的青睐。函数式编程是一种强调减少对程序外部状态产生改变的方式。因此,...
阅读 1683·2021-11-23 09:51
阅读 3173·2021-09-26 10:21
阅读 797·2021-09-09 09:32
阅读 880·2019-08-29 16:06
阅读 3307·2019-08-26 13:36
阅读 771·2019-08-26 10:56
阅读 2563·2019-08-26 10:44
阅读 1142·2019-08-23 14:04