JavaScript是基于原型的? 嗯。
那new、this是什么? 额。。。
什么是原型?
照猫画虎。
你要建一座房,照着设计图建就是“面向对象”,照着邻居建就是“基于对象”。
之前提到过,基于原型就是基于已有的对象,描述不一样的特征,所以相同的特征就直接“复制”过来。
基于原型的面向对象系统通过“复制”的方式来创建新对象。一些语言的实现中,还允许复制一个空对象。这实际上就是创建一个全新的对象。
Brendan的设计JS的思路是:
1 | 1. 借鉴C语言的基本语法 |
原型的”复制”实现:
- 新对象引用原型
- 完整地复制一份,两个对象不再有关联
JavaScript用的是第一种
JavaScript的原型
简单地说
- 对象有私有字段
[[prototype]]
,就是原型 - 查找一个属性,当前对象没有,继续访问对象原型,直到空或者找到
ES6中提供三个操作原型的API
Object.create(proto[, propertiesObject])
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__Object.getPrototypeOf(object)
返回指定对象的原型Object.setPrototypeOfObject.setPrototypeOf(obj, prototype)
设置一个指定对象的原型
使用原型抽象猫和虎
1 | var cat = { |
当然,这是使用ES6语法操作的原型。再来看一下之前的原型操作
早期的原型
1. ES3之前的
早期的“类”是一个私有属性[[class]]
,内置类型如Number、String、Date等指定[[class]]
属性,唯一可以访问[[class]]
的方式是Object.prototype.toString
1 | var o = new Object; |
2. ES5之后
[[class]]
被Symbol.toStringTag替代,[[Symbol.toStingTag]]
可以自定义Object.prototype.toString
的行为
1 | var o = { [Symbol.toStringTag]: "MyObject" } |
3. new、this
当然,JavaScript作为Brendan Eich在网景公司任职时创造的成果,受到公司政治的影响,被要求模仿JAVA(当时的JAVA Applet真是太。。。),引入了new
、this
。
1 | function c1(){ |
new
干了什么
- 以构造器的
prototype
属性(不是私有字段[[prototype]]
)为原型,创建新对象 - 将this和调用参数传给构造器,执行
- 若构造器返回的是对象,返回;否则,返回第一步创建的新对象
像上面的代码一样,new使得函数对象变得和”类”相似,但却提供了两种方法:
- 直接在
constructor
中修改this,给this添加属性 - 修改
constructor
的prototype
属性指向的对象,他是从该constructor
构造出来的所有对象的原型
所以,Object.create
算是new
的语法糖??反了吧?按时间,好像是合理的。
1 | // 用new实现 Object.create |
ES6之前(没有Object.create
和Object.setPrototypeOf
),运算符new
是唯一一个可以指定[[prototype]]的方法(不是所有环境都支持__proto__
)
4. ES6中的类
ES6引入了class关键字,并且在标准中删除了所有[[class]]
相关的私有属性描述。
所以,当用“类”的思想来写代码时,就要用class
而不是function
来模拟;让function
回归原本的函数语义。(那就清晰了)
1 | class Rectangle { |
what’s more
类提供了继承能力
1 | class Animal { |
调用子类的speak
方法获取到父类的name
Object API / Grammar分类
{}
、.
、[]
、Object.defineProperty
增改查对象Object.create
、Object.setPrototypeOf
、Object.getPrototypeOf
原型操作new
、class
、extends
类操作new
、function
、prototype
历史包袱,别用了
虽然class在编译后仍是基于原型的,但从语法和抽象能力来看,属于“分类”的面向对象思路