在学习js面向对象时,每次学了不运用,不过多久就会忘,所以今天跟着阮一峰老师的理解记下笔记,以下为学习时自己的理解。
1.封装
一、构造函数的实例对象的constructor指向构造函数
1 | function Cat(name,color){ |
其实这里是有问题的,cat1.constructor == Cat返回为true的原因是:cat1并没有constructor属性,但它的原型链Cat.prototype上有constructor属性且指向Cat构造函数。当我们访问cat1.constructor时它自身没有就往原型链上找,一找就找到了Cat.prototype,于是cat1.constructor == Cat返回true。
二、构造函数有prototype属性,指向一个对象,这个对象的所有属性和方法都会被构造函数的实例继承
1 | Cat.prototype.type = "猫科动物"; |
这时每个实例的的type和action都是指向同一内存地址,也就是prototype对象,节省了内存。
当实例改变了prototype上的属性时,只会改变当前实例的属性,prototype的属性并不会更改,也就不会影响其他实例,方法同上,以下也只说属性,方法同样。
原因:既然实例会继承prototype指向的对象,于是cat1.proto == Cat.prototype,当访问实例属性时,先会在实例上找有没有该属性,没有的话便顺着原型链往上一级找,上一级也就是Cat.prototype。当实例改变了prototype上的属性(cat1.type),相当于在实例上添加了属性,并不会改变原型链上祖辈的属性,于是下次访问该属性(cat1.type)时,在实例上找到了该属性,便不会再往原型链上级找。
1 | cat1.type = "哺乳动物"; |
三、几个实用的验证方法
1 | isPrototypeOf() 验证实例与prototype之间的关系 |
2.构造函数的继承
一、构造函数绑定
用call或者apply的方法将父对象的构造函数绑定在子对象的构造函数中。这种方法只能继承构造器里的属性和方法
1 | function Animal(){ |
二、prototype修改
将子对象的构造函数的prototype直接指向父对象的构造函数的实例。由于直接修改了prototype指向的对象,导致prototype的constructor的值变为父对象的构造函数,每当访问实例或者prototype的constructor属性,都会调用prototype对象的constructor属性,这样当然会破坏constructor的原本设计初衷,于是也要讲prototype的constructor重新指向子对象的构造函数。这种方法能继承构造器里的属性和方法,也能继承原型链上的属性和方法
1 | Cat.prototype = new Animal(); |
三、直接赋值prototype
将父对象的prototype直接赋值给子对象的prototype,实现了继承,这种方法有一个很大的特性就是Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。这种方法只能继承原型链上的属性和方法
1 | function Animal(){ } |
四、利用空对象作为中介
由于”直接继承prototype”存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。这种方法只能继承原型链上的属性和方法
1 | var F = function(){}; |
我们将上面的方法,封装成一个函数,便于使用。这个extend函数,就是YUI库如何实现继承的方法
1 | function extend(Child, Parent) { |
五、拷贝继承
把父对象的所有属性和方法,拷贝进子对象,也能够实现继承。这种方法只能继承原型链上的属性和方法
1 | function Animal(){} |
3.非构造函数的继承
一、object()方法
这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。类似第二节里《四、利用空对象作为中介》
1 | let Chinese = { |
二、浅拷贝
除了使用”prototype链”以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。类似第二节里《五、拷贝继承》
1 | function extendCopy(parent){ |
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
1 | Chinese.birthPlaces = ['北京','上海','香港']; |
所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。这是早期jQuery实现继承的方式。
三、深拷贝
所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。
1 | function deepCopy(p, c) { |
目前,jQuery库使用的就是这种继承方法。
4.class继承
新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),这样就避免了Student.prototype.hello = function () {…}这样分散的代码。
1 | class Student{ |