首页 > 编程语言 >通俗的英文指南——Javascript的原型

通俗的英文指南——Javascript的原型

时间:2022-12-08 13:07:43浏览次数:71  
标签:指南 __ kind JavaScript zack Javascript person 原型 通俗


​ http://www.w3cplus.com/js/a-plain-english-guide-to-javascript-prototypes.html ​​​
当我开始学习JavaScript的对象模型时,第一反应就是难以置信。我完全被它的原型本质给弄糊涂了,毕竟这是我头一次遇到以原型为基础的语言。因为JS中有构造函数这个概念,所以我看不出使用原型能给JS带来任何的好处。我敢说你们中的大部分人也有同样的经历。

但是当我更多的使用JavaScript后,我不仅开始理解它的对象模型,甚至还喜欢上了它的一部分。感谢JavaScript让我见识到了原型语言的优雅与灵活。现在的我十分推崇原型语言,原因就是相对于以类为基础的语言,原型语言的有着更简单、更灵活的对象模型。

JavaScript中的原型

绝大部分的指南或者教程在开始讲解JavaScript对象时,总是直接从「构造函数」讲起。我觉得这是错误的,因为过早的引入相对复杂的概念,只能让JavaScript看起来更复杂,学起来更迷惑。所以让我们暂时抛开这个概念,先讲讲原型的基础。

原型链(又叫做原型继承)

JavaScript中的所有对象都有原型。当对象收到一个消息(译者注:在这里可以理解为属性查找或方法调用),JavaScript便尝试先从对象自身查找属性,如果查不到,那么该消息就会传递给对象的原型并以此类推。这个工作机制很像以类为基础的语言中的单根继承。

原型链的长度没有限制,但通常来说,过长的原型链会造成代码维护与理解上的困难,因此不值得推荐。

__proto__对象

理解JavaScript原型链最简单的方式就是通过__proto__属性。可惜的是,在ES 6之前,__proto__并不是JavaScript中的标准接口,所以一定不要在工作代码中使用它。尽管有这样的限制,这个属性使我们讲解原型更容易了。

// 让我们先创建一个alien对象

var alien = {

kind: 'alien'

}


// 然后是一个person对象

var person = {

kind: 'person'

}


// 最后是一个叫做zack的对象

var zack = {};


// 设置alien为zack的原型

zack.__proto__ = alien


// zack现在与alien关联了起来

// 它继承了alien的所有属性

console.log(zack.kind); //=> ‘alien’


// 接下来将person设置为zack的原型

zack.__proto__ = person


// 这时候zack又和person关联在一起了

console.log(zack.kind); //=> ‘person’



如你所见,__proto__属性的含义与用法简单明了。即便我们不能在工作的代码中使用__proto__,但我想上面那些例子已经为你理解JavaScript对象模型打下了坚实的基础。

你可以像下面这么操作,来判断一个对象是否是另一个对象的原型:

console.log(alien.isPrototypeOf(zack))
//=> true
原型查找是动态的

你可以在任何时候为原型增加新属性,原型链查找机制会如你所愿找到这些新属性。

var person = {}

var zack = {}
zack.__proto__ = person

//当前zack没有kind属性
console.log(zack.kind); //=> undefined

// 让我们在person上增加kind属性
person.kind = 'person'

// 现在有反应了,因为它从person上找到了kind属性
console.log(zack.kind); //=> 'person'
新建/更新的属性被赋值给了对象,而不是它的原型

如果你更新一个在原型上已经存在的属性会发生什么?我们来试试:

var person = {

kind: 'person'

}


var zack = {}

zack.__proto__ = person


zack.kind = 'zack'


console.log(zack.kind); //=> 'zack'

// zack 现在拥有kind属性


console.log(person.kind); //=> 'person'

// person上的属性没有被修改

注意:「kind」属性已经同时存在于person和zack上了。


Object.create


前面解释过使用__proto__为对象设置原型这个办法并不通用。所以我们介绍另外一种简单方法:Object.create()。这是在ES 5中新增的方法,对于那些古老的浏览器或者JS引擎,可以使用es5-shim来模拟。

var person = {

kind: 'person'

}


// 创建一个原型为person的新对象

var zack = Object.create(person);


console.log(zack.kind); // => ‘person’


你可以向Object.create()方法中传入一个对象,用来给新生成的对象设置特定的属性:

var zack = Object.create(person, {age: {value: 13} });
console.log(zack.age); // => ‘13’
是不是觉得传进去的对象很麻烦?实际上它必须是这个样子。详细信息可以查看文档。

Object.getPrototype

可以使用Object.getPrototypeOf()方法来获得对象的原型:

ar zack = Object.create(person);
Object.getPrototypeOf(zack); //=> person
这里可没有Object.setPorototype这样设置原型的方法。

构造函数

在JavaScript中,构造函数是用来创建原型链最常用的方法。构造函数如此流行的原因在于:它是创建类型的唯一途径。另外一个值得考虑的因素是大部分引擎都对构造函数进行了高度优化。

很不幸的是,构造函数容易让人困惑,在我看来,它是让初学者认为JavaScript难以理解的主要原因。但构造函数是JS语言中的一个重要部分,我们必须对其深刻理解。

作为构造器的函数

在JavaScript中,创建函数的实例可以这么来做:

function Foo(){}

var foo = new Foo();

//foo 现在是Foo的一个实例
console.log(foo instanceof Foo ) //=> true
对函数使用关键字new,使得函数从本质上来讲表现的像工厂,即函数能够生成新的对象。后面会讲到,新创建的对象通过函数的原型与函数保持关联。在JavaScript中,我们将这个对象称为函数的实例。

隐式赋值的「this」

当我们使用「new」,JavaScript以「this」关键字的形式向函数中注入了新创建对象的隐式引用。在函数运行结尾处也会隐式的返回该引用。

当我们这么做:

function Foo() {
this.kind = ‘foo’
}

var foo = new Foo();
foo.kind //=> ‘foo’
实际上会相当于这么做:

function Foo() {
var this = {}; // 这里的this是无效的,我们这么做只是为了演示
this.__proto__ = Foo.prototype;

this.kind = ‘foo’

return this;
}
需要注意的是:只有在使用「new」的时候,「this」才会被赋值为新创建的对象。如果你忘记书写「new」,那么「this」会指向全局对象。忘记new是造成众多bug的原因,所以千万不要忘记写new。

有一个惯例我十分喜欢,那就是如果一个函数被用作构造函数,那么它的函数名首字母要大写,如果你忘记写new关键字,就会很容易发觉。

「函数原型」

JavaScript中的每个函数都有一个特殊属性「prototype」。

function Foo(){
}

Foo.prototype
让人有些不敢相信的是:此「prototype」并非函数真正的原型(__proto__)。

foo.__proto__ === foo.prototype //=> false
如果「prototype」术语的含义如此不明确,可想而知会对使用它的人们产生多大的困扰。有一个澄清的方法我认为不错,那就是始终将函数的「prototype」属性称为「函数的原型」,永远不要称其为「原型」。

「prototype」属性指向一个对象,这个对象就是当对函数使用「new」时创建的实例的原型。听起来糊涂?用例子来解释是最容易不过的了:

function Person(name) {
this.name = name;
}

// 函数person拥有一个prototype属性
// 我们可以为函数的原型增加新属性
Person.prototype.kind = ‘person’

// 当我们使用new来生成新对象时
var zack = new Person(‘Zack’);

// 新对象的原型将指向person.prototype
zack.__proto__ == Person.prototype //=> true

// 在这个新对象中,我们能够访问在Person.prototype上定义的属性
zack.kind //=> person

标签:指南,__,kind,JavaScript,zack,Javascript,person,原型,通俗
From: https://blog.51cto.com/u_14230175/5920953

相关文章

  • 四、程咬金——JavaScript基础
     一、前言Ajax,异步JavaScript和XML,前面已经解释过,所以Ajax的学习还包含JavaScript和XML,这里我们先看JavaScript,而JavaScript实际上又是包含JavaScript语法和HTMLDOM即文档......
  • 《悟透javascript》学习笔记
    《悟透javascript》学习笔记 一、         前言 二、         回归简单、基本类型 三、         表演、似类却不是类 四、      ......
  • 《悟透javascript》学习笔记:X、深入继承
    引言      JavaScript不是按面向对象的思想设计的程序语言,所以它不具备像现有的面向对象的语言那样的功能,但是面向对象的思想是如此的深入人心,以至于JavaScript也削......
  • 再读《悟透javascript》之五、五子棋
    前言     五子棋是个很有趣的游戏,在用javascript开发之后,我发现其实ai算法才是最难的,这里的ai算法是直接借鉴自其它的ai算法。  代码如下:<htmlxmlns="http://www.w3......
  • 《悟透javascript》学习笔记:四、函数的魔力
    引言 JavaScript的代码就只有function一种形式,function就是函数的类型。也许其他编程语言还有procedure或method等代码概念,但在JavaScript里只有function一种形......
  • 再读《悟透javascript》之四、贪吃蛇
    前言     贪吃蛇是个很简单的小游戏,但是却很有趣,下面是我用JavaScript写的一个贪吃蛇的代码:  代码如下:   <htmlxmlns="http://www.w3.org/1999/xhtml"><headr......
  • 《悟透javascript》学习笔记:一、前言
    《悟透JavaScript》学习笔记  这是一本很形象生动的书,使我们可以更深地了解了JavaScript。 引言   编程世界里只存在两种基本元素,一个是数据,一个是代码。编程世界就......
  • 《悟透javascript》学习笔记:二、回归简单
    引言 要理解JavaScript,你得首先放下对象和类的概念,回到数据和代码的本原。前面说过,编程世界只有数据和代码两种基本元素,而这两种元素又有着纠缠不清的关系。JavaScript就......
  • 《悟透javascript》学习笔记:三、似类却不是类
    引言 JavaScript没有“类”的概念!这让很多学习过高级语言的人感到很不爽,其实比之于C语言又如何呢?显然JavaScript要比C语言好用很多。 不废话,上代码 <scriptlanguage="j......
  • 再读《悟透javascript》之三、甘露模型
    前言     甘露模型是用于开发基于javascript的类库的,通过它,我们可以以类似C#等面向对象的语言式的模式来开发javascript类库,这将使你的javascript代码变得清晰有条理......