原型
JS
里万物皆对象,而对象是由构造函数创建的。基本数据类型都有与之对应的构造函数。如number
对应的是Number
,当你定义一个变量并存储一种数据类型时(变量没有类型,数据有类型),会有相应的数据类型的方法,这些方法从哪里来?其实就是从原型来的。
1 | var a = 1; // 定义一个数字型 |
每个对象在创建时内部都有一个内置属性[[Prototype]]
指向原型,在浏览器中可以用__proto__
来表示(注意这个不是标准属性,有些浏览器不存在此属性),这个原型是指构造函数中的Function.prototype
,也就是同一个构造函数创建的实例对象都有同样的原型,原型对象中的方法和属性是所有实例对象所共有的。原型中有一个属性是指向构造函数的Function.prototype.constructor === Function
1 | function Person(name) { |
原型是可以修改的。
原型链
原型也是对象,对象就有原型,这样每个对象内的原型也有自己的原型,这样下去每个原型就可以形成一个链条,这就是原型链。但这个链的尽头在哪里?JS
里万物虽然都是对象,但这些对象也是有源头的,所有创建这些对象的构造函数的原型都是指向Object.prototype
,而Object.prototype
的原型是null
也就是说这个Object
是最基本的构造函数。
这个原型链有啥用呢?
看个例子:
定义一个对象,对象有个属性和一个方法,正常来说对象里没定义的属性或方法,调用的时候属性会返回undefined
,方法会抛出一个错误, 我们调用一个obj.valueOf()
试试,它是有值返回的,没有报错,说明这个obj
对象里有这个方法,那个这个方法是怎么来的呢?
先来看下对象是怎么创建的。对于JS
来说创建对象有两种形式。
- 字面量,也是推荐的一种,这是一种简写方式,其实内部还是通过相应的构造函数
new
出来的。 - 通过相应构造函数创建一个实例对象。这种比较麻烦,一般不推荐这种方法。
前面说过了,在创建对象过程中,对象有个内部属性指向构造函数的原型,这个过程在后面说,具体就是obj.__proto__ = Object.prototype
。这个原型上有些方法。
当obj.val
取值时,发现当前对象,没有这个属性,它会找到原型,看下原型有没有这个属性,如有就返回这个属性,没用就继续往原型的原型上找,直到没有原型为止,如果一直找不到这个属性就会返回undefined
。方法也是如此。
如valueOf
方法,不在obj
对象上,去原型obj.__proto__
上找,也就是指向的Object.prototype
这个对象上有valueOf
方法,调用。如果这个对象没有,而这个对象的原型是null
没有,就是返回undefined
这就是经常的报错 undefined is not function
。
给对象的属性赋值时,属性存在正常赋值,如果属性不存在,它不会往它的原型链上找。也就是说,当给对象属性赋值时,在当前对象找,找到一 直接赋值,找不到直接就创建一个新的属性了,不会往原型链上找到赋值,这也是可以理解的,整条原型链,如果这样操作,所有创建的属性都往最顶级的原型去,那不是很大很大?
1 | // 字面量 |
这个原型链有啥用呢?
当开发面向对象编程的时候,就可以通过原型链来封装和继承一些公共的属性或方法了。
(作用域链,其实就是变量的寻找过程,和这个原型链类似)
继承
什么是继承?前面说了,原型是可以修改的,就是在创建对象的时候,把带有公共属性的对象指向创建对象的原型,这样新对象就可以继承原型对象上的方法或属性了,继承就是通过原型链的形式来实现。
继承有好几种方式可以实现,每种方式都各有优缺点,这里就不说了,网上很多,这里附上两个不错的地址。
1 | // 父类 |
ES6
和ES5
在继承上的区别,上面是ES5
的,来看下ES6
的实现方式。ES6
上的继承是先有父类的实例,才有子类的实例,而ES5
则恰好相反,先有子类才有父类。
1 | class Parent {} |
new实现
new
一个构造函数时主要过程有四个:
- 创建一个新的对象。
- 把构造函数的原型赋值给新对象的内部属性。
- 判断
this
并且执行构造函数给this
的属性赋值。 - 返回一个新对象或者是返回构造函数返回的对象。
注意:当构造函数返回对象时,this
指向失效了。 看下面代码就知道了。
1 | function new(Fn, ...args) { |
instanceof实现
typeof
判断的是数据类型,而instanceof
是一个操作符。left instanceof Right
是判断Right
构造函数的原型Right.prototype
是否存在left
的原型链,存在返回true
,否则返回 false
。
1 | function instanceof(left, right) { |
原型一直是
JS
的重点,很多时候都记不住,现在把自己了解的记下来,并把原型相关的两个new,instanceof
关键字的实现一下,方便一起记忆。