这篇文章主要帮助大家理解 javascript 对象继承,先从一个问题出发,引入 javascript 对象继承相关知识,感兴趣的小伙伴们可以参考一下
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
先从一个问题进行研究深入,什么是 javascript 对象继承?
比如我们有一个 "动物" 对象的构造函数。
- function animal() {
- this.type = '动物';
- }
还有一个 "猫" 对象的构造函数。
- function cat(name,color) {
- this.name = name;
- this.color = color;
- }
我们知道猫也属于动物,如果这个猫对象想要继承动物对象的属性,我们该怎么做呢?
构造函数绑定
使用构造函数绑定是最简单的方法,使用 call 或者 apply 将父对象绑定在自对象上就可以了。
- function cat(name,color) {
- animal.apply(this,arguments);
- this.name = name;
- this.color = color;
- }
- var cat1 = new cat("haha", 'red');
- console.log(cat1.type); //动物
不过这种方法比较少见。
拷贝继承
如果把父对象的所有属性和方法,拷贝进子对象,也可以实现继承。
- function extend(Child, Parent) {
- var p = Parent.prototype;
- var c = Child.prototype;
- for (var i in p) {
- c[i] = p[i];
- }
- c.uber = p; //桥梁作用
- }
使用方法:
- extend(cat, animal);
- var cat1 = new cat("haha","red");
- alert(cat1.type); // 动物
原型继承(prototype)
相比于上面的直接绑定,原型继承的方法比较常见,对于 prototype,我自己简单总结了一下。
每个函数都有一个 prototype 属性,这个属性是指向一个对象的引用,当使用 new 关键字创建新实例的时候,这个实例对象会从原型对象上继承属性和方法。
也就是说,如果将 "猫" 构造函数的 prototype 属性指向一个 "动物" 实例,那么再创建 "猫" 对象实例的时候,就继承了 "动物" 对象的属性和方法了。
继承实例
- cat.prototype = new animal();
- cat.prototype.constructor = cat;
- var cat1 = new cat("haha","red");
- console.log(cat1.constructor == cat); //true
- console.log(cat1.type); // 动物
1、代码第一行,我们将 cat 函数的 prototype 对象指向一个 animal 对象的实例(其中就包含了 animal 的 type 属性了)。
2、代码第二行是什么意思呢?
1)、首先,假如我们没有加这行代码,运行
cat.prototype = new animal();
console.log(cat.prototype.constructor == animal); //true
也就是说,其实每个 prototype 对象都有一个 constructor 属性,指向它的构造函数。
2)、我们再看下面的代码
- cat.prototype = new animal();
- var cat1 = new cat("haha", 'red');
- console.log(cat1.constructor == animal); //true
由上我们看到实例 cat1 的构造函数是 animal,所以,显然是不对的。。。cat1 明明是 new cat() 才生成的,所以我们应该手动纠正。cat.prototype 对象的 constructor 值改为 cat。
3)、所以这也是我们应该注意的一点,如果我们替换了 prototype 对象,就应该手动纠正 prototype 对象的 constructor 属性。
o.prototype = {};
o.prototype.constructor = o;
直接继承 prototype
由于在 animal 对象中,不变的属性可以直接写在 animal.prototype 中。然后直接让 cat.prototype 指向 animal.prototype 也就实现了继承。
现在我们先将 animal 对象改写成:
- function animal() {
- }
- animal.prototype.type = '动物';
然后再实现继承:
- cat.prototype = animal.prototype;
- cat.prototype.constructor = cat;
- var cat1 = new cat("haha","red");
- console.log(cat1.type); // 动物
与上一种方法相比,这种方法显得效率更高(没有创建 animal 实例),节省了空间。但是这样做正确吗?答案是不正确,我们继续看。
cat.prototype = animal.prototype;
这行代码让 cat.prototype 和 animal.prototype 指向了同一个对象,所以如果改变了 cat.prototype 的某一个属性,都会反映到 animal.prototype 上,这显然不是我们想要看到的。
比如我们运行:
console.log(animal.prototype.constructor == animal) //false
结果看到是 false,为什么呢?cat.prototype.constructor = cat; 这一行就会把 animal.prototype 的 constructor 属性也改掉了。
利用空对象作为中介
- var F = function(){};
- F.prototype = animal.prototype;
- cat.prototype = new F();
- cat.prototype.constructor = cat;
结合上面两种方法,因为 F 是空对象,所以几乎不占内存。这时修改 cat 的 prototype 对象,就不会影响到 animal 的 prototype 对象。
console.log(animal.prototype.constructor == animal); // true
然后我们将上面的方法封装一下:
- function extend(Child, Parent) {
- var F = function(){};
- F.prototype = Parent.prototype;
- Child.prototype = new F();
- Child.prototype.constructor = Child;
- Child.uber = Parent.prototype;
- }
使用的时候,方法如下:
- extend(cat,animal);
- var cat1 = new cat("haha","red");
- console.log(cat1.type); // 动物
Child.uber = Parent.prototype; 这行代码就是个桥梁作用,让子对象的 uber 属性直接指向父对象的 prototype 属性,等于在自对象上打开一条叫 uber 的通道,让子对象的实例能够使用父对象的所有属性和方法。
以上就是对 javascript 对象继承我的理解,希望或多或少能够帮助到大家,谢谢大家的阅读。
来源: http://www.phperz.com/article/17/0224/266284.html