一步一步捋清prototype
前言
- 学习JavaScript,不可避免地要学习JS中的面向对象的编程模式,但JS中的OOP和c++,java等典型的面向对象语言明显不同,“原型链”,es6中的“class”,都彰显着js中面向对象的与众不同。
- 究竟什么是原型prototype呢?如何在JavaScript中实现“类”呢?
一张图
图片分析
这是我总结的一张图片,简单的描绘了原型和构造函数、实例间的复杂关系。
- 全局对象window属性Object(此处仅用来举例,其余属性暂且不提),指向Object()这个函数对象
- Object中存着prototype,它指向了原型(原型中包含了对象的所有方法和属性)
- 当我们声明一个实例
let obj = new Object()
, 你可以发现它天生就带着__proto__属性。而__proto__竟然和Object.prototype
指向了同一个对象。我们知道,它就是原型。
一些思考
1.Object.prototype
(即对象的原型)的原型是什么呢?
- 我们不妨打印出来试一试:
应该和你想的一样,对象的原型就是原型链的尽头了,它只是一个普通的对象,并没有原型。
2.Object.prototype
中的constructor是什么?有什么用?
- 你应该还记得我们刚刚像这样
let obj = new Object();
创建了一个对象实例obj,那么再来看看这个,你应该就一目了然了
没错,constructor存在的意义就是为了让实例化地对象找到它的构造函数。(对实例化的数组,constructor指向Array;对实例化地函数,constructor指向Function)
3.但你不能人为constructor指向的一定是构造出当前对象的函数。
- 例如,你能说出Object.prototype.constructor指向谁吗?
你看,这个结果是不是很让人头大??Object.prototype
指向了原型,而原型的constructor的又指回了Object。实际上这种试验是无意义的,因为我们要知道,constructor是提供给实例,用来定位它的构造函数的属性,而不是给原型本身使用的。 - 而这种Object.prototype.constructor ===Object的现象,其实源于人为设计。为了让实例的对象都继承原型中的constructor,无法避免出现这种现象。
4.我们尝试更深入一些?尝试剖析一下数组对象的相关结构。
-
问题1:Array.prototype.__proto__===?
- 直接来看结果吧
- 为什么会有这种结果呢?其实我们只需要理解清Array.prototype的本质就好了,其实Array.prototype只是一个Object()构造出的对象实例而已,一个对象实例的原型当然是Object.prototype啊!换句话说Array.prototype和obj在本质上是一样的!
- 直接来看结果吧
-
问题2:let arr=[1,2,3];
属于对象的方法hasOwnProperty是如何在数组实例上生效的呢?
- arr在自身寻找hasOwnProperty方法,发现没有。
- arr又到他爸爸arr.__proto__即Array.prototype中寻找hasOwnProperty方法,仍然找不到 =
- arr再到他爷爷arr.__proto__.__proto__即Object.prototype中寻找,找到了这个方法,于是arr调用了这个方法。
类和继承
-
class的默认属性
class Person { name = 'Oliver' sayHi = ()=>{ console.log(hi) } }
在class声明中
通过=
赋值的属性,会变成该class实例的属性默认值;
通过箭头函数声明的方法,会挂载到该实例身上,而非class身上。 -
方法的重写
// Person定义同上 class Child extends Person{ sayHi(){ console.log('I am a child.') super.sayHi() } }
子类重写父类的方法后,如果还想调用父类的方法,需要使用
super
关键字,super上挂载有父类的原始方法。 -
class vs prototype
// prototype方法 function Person( name, age) { this.name = name, this.age = age } Person.prototype.sayHi = function() { console.log('你好,我叫'+this.name); } //class方法 class Person{ constructor(name,age){ this.name = name; this.age = age; } sayHi(){ console.log('你好,我叫'+this.name); } }
-
继承
class Children extends Person{ constructor(name,age,grade){ super(name,age); this.grade = grade; } sayGrade(){ console.log('我今年${grade}年级'); } }
conclusion
- class只是prototype方法的语法糖而已,本质上没有区别
- 运用顺序:理解prototype => 熟练使用prototype => 无压力使用class
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。