Javascript 是一种基于对象 (object-based) 的语言, 你遇到的所有东西几乎都是对象但是, 它又不是一种真正的面向对象编程 (OOP) 语言, 因为它的语法中没有 class(类)
那么, 如果我们要把 "属性"(property)和 "方法"(method), 封装成一个对象, 甚至要从原型对象生成一个实例对象, 我们应该怎么做呢?
一生成实例对象的原始模式
这就是最简单的封装了, 把两个属性封装在一个对象里面但是, 这样的写法有两个缺点, 一是如果多生成几个实例, 写起来就非常麻烦; 二是实例与原型之间, 没有任何办法, 可以看出有什么联系
二原始模式的改进
这种方法的问题依然是, cat1 和 cat2 之间没有内在的联系, 不能反映出它们是同一个原型对象的实例
三构造函数模式
所谓 "构造函数", 其实就是一个普通函数, 但是内部使用了 this 变量对构造函数使用 new 运算符, 就能生成实例, 并且 this 变量会绑定在实例对象上
这时 cat1 和 cat2 会自动含有一个 constructor 属性, 指向它们的构造函数
Javascript 还提供了一个 instanceof 运算符, 验证原型对象与实例对象之间的关系
四构造函数模式的问题
构造函数方法很好用, 但是存在一个浪费内存的问题
请看, 我们现在为 Cat 对象添加一个不变的属性 type(种类), 再添加一个方法 eat(吃)那么, 原型对象 Cat 就变成了下面这样:
表面上好像没什么问题, 但是实际上这样做, 有一个很大的弊端那就是对于每一个实例对象, type 属性和 eat()方法都是一模一样的内容, 每一次生成一个实例, 都必须为重复的内容, 多占用一些内存这样既不环保, 也缺乏效率
五 Prototype 模式
Javascript 规定, 每一个构造函数都有一个 prototype 属性, 指向另一个对象这个对象的所有属性和方法, 都会被构造函数的实例继承
这意味着, 我们可以把那些不变的属性和方法, 直接定义在 prototype 对象上
这时所有实例的 type 属性和 eat()方法, 其实都是同一个内存地址, 指向 prototype 对象, 因此就提高了运行效率
六 Prototype 模式的验证方法
- isPrototypeOf()
- hasOwnProperty()
in 运算符
构造函数的继承
怎样才能使 "猫" 继承 "动物" 呢?
一构造函数绑定
第一种方法也是最简单的方法, 使用 call 或 apply 方法, 将父对象的构造函数绑定在子对象上, 即在子对象构造函数中加一行:
二 prototype 模式
第二种方法更常见, 使用 prototype 属性
如果 "猫" 的 prototype 对象, 指向一个 Animal 的实例, 那么所有 "猫" 的实例, 就能继承 Animal 了
三直接继承 prototype
第三种方法是对第二种方法的改进由于 Animal 对象中, 不变的属性都可以直接写入 Animal.prototype 所以, 我们也可以让 Cat()跳过 Animal(), 直接继承 Animal.prototype
现在, 我们先将 Animal 对象改写:
然后, 将 Cat 的 prototype 对象, 然后指向 Animal 的 prototype 对象, 这样就完成了继承
与前一种方法相比, 这样做的优点是效率比较高(不用执行和建立 Animal 的实例了), 比较省内存缺点是 Cat.prototype 和 Animal.prototype 现在指向了同一个对象, 那么任何对 Cat.prototype 的修改, 都会反映到 Animal.prototype
Cat.prototype.constructor = Cat, 这一句实际上把 Animal.prototype 对象的 constructor 属性也改掉了!
四利用空对象作为中介
由于 "直接继承 prototype" 存在上述的缺点, 所以就有第四种方法, 利用一个空对象作为中介
F 是空对象, 所以几乎不占内存这时, 修改 Cat 的 prototype 对象, 就不会影响到 Animal 的 prototype 对象
我们将上面的方法, 封装成一个函数, 便于使用
使用的时候, 方法如下:
五拷贝继承
上面是采用 prototype 对象, 实现继承我们也可以换一种思路, 纯粹采用 "拷贝" 方法实现继承简单说, 如果把父对象的所有属性和方法, 拷贝进子对象, 不也能够实现继承吗? 这样我们就有了第五种方法
首先, 还是把 Animal 的所有不变属性, 都放到它的 prototype 对象上
然后, 再写一个函数, 实现属性拷贝的目的
这个函数的作用, 就是将父对象的 prototype 对象中的属性, 一一拷贝给 Child 对象的 prototype 对象
使用的时候, 这样写:
非构造函数的继承
一什么是 "非构造函数" 的继承?
请问怎样才能让 "医生" 去继承 "中国人", 也就是说, 我怎样才能生成一个 "中国医生" 的对象?
这里要注意, 这两个对象都是普通对象, 不是构造函数, 无法使用构造函数方法实现 "继承"
二 object()方法
这个 object()函数, 其实只做一件事, 就是把子对象的 prototype 属性, 指向父对象, 从而使得子对象与父对象连在一起
三浅拷贝
除了使用 "prototype 链" 以外, 还有另一种思路: 把父对象的属性, 全部拷贝给子对象, 也能实现继承
下面这个函数, 就是在做拷贝:
使用的时候, 这样写:
但是, 这样的拷贝有一个问题那就是, 如果父对象的属性等于数组或另一个对象, 那么实际上, 子对象获得的只是一个内存地址, 而不是真正拷贝, 因此存在父对象被篡改的可能
所以, extendCopy()只是拷贝基本类型的数据, 我们把这种拷贝叫做 "浅拷贝" 这是早期 jQuery 实现继承的方式
四深拷贝
所谓 "深拷贝", 就是能够实现真正意义上的数组和对象的拷贝它的实现并不难, 只要递归调用 "浅拷贝" 就行了
使用的时候这样写:
目前, jQuery 库使用的就是这种继承方法
来源: http://www.qdfuns.com/note/22516/4071727f0e95a164fc01d53bcea04934.html