Javascript 对象继承

属性的继承:调用父类的构造函数call

1
2
3
4
5
6
7
8
9
10
11
function Person(name, sex){
this.name = name;
this.sex = sex;
}
//创建一个明星类,它继承自Person类
function Star(name, sex, age){
Person(name, sex);
this.age = age;
}
var s = new Star("小名", "男", 20);
console.log(s);

此时我们发现输出的s并没有name、sex属性,在Star类中是window对象调用的Person类,这是的name和sex属性都赋给了window对象。这里我们要修正this的指向。修改第7行代码如下:

1
Person.call(this, name, sex);

方法的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.showName = function(){
console.log( this.name );
}
var p = new Person("小红", "女");
//创建一个明星类,它继承自Person类
function Star(name, sex, age){
Person.call(this, name, sex);
this.age = age;
}
//继承方法
Star.prototype = Person.prototype;
var s = new Star("小名", "男", 20);
console.log(s);

这时我们发现了一个问题,当你把一个原型对象赋给另一个原型对象时他们是按引用传递。这样的话,这两个对象的修改都会互相影响。

我们现在Star原型上添加一个方法,看看会不会影响到Person。

1
Star.prototype.showJob = function(){}


我们发现在Person下也有showJob方法,这并不是我门希望得到的结果。

现在我们想到如果是一个基本类型的赋值就就不会出现这种问题,我们写个简单的继承方法,通过for in 遍历要继承对象的属性,然后一个一个赋值给父类对象。

1
2
3
4
5
6
extend(Star.prototype, Person.prototype);
function extend(son, father){
for(var attr in father){
son[attr] = father[attr];
}
}

这样给Star添加方法就不会影响到Person对象。

这时悉心的同学有会发现问题:虽然属性可以复制,但是在Javascript中函数也是对象,函数赋给函数也存在引用关系?

现在来解释一下函数也是对象为什么就没有问题:虽然函数也是对象类型,但是它与对象类型还是有区别的。函数只能被重新赋值,不能被修改。

现在我们来讲解一下重新赋值与修改的区别。

1
2
3
4
var a = [1,2,3];
var b = a;
b.push(4); // a => [1,2,3,4]
b = [1,2,3] // a => [1,2,3,4]

第3行修改了b,因为b是a的引用,所以a也被修改了。第4行给b重新赋值了,b由指向新的对象,对a并没有影响。


当然,上面的继承方法还是比较简单,现在我们来分析一下jquery的继承方法,有不正确的地方欢迎指正。

jquery-2.1.3.js extend源码。官方extend文档
jQuery.extend( [deep ], target, object1 [, objectN ] )

  • deep:如果是true,就进行递归拷贝。
  • target:继承的对象。
  • object1、objectN :被继承的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
//获取实参的个数
length = arguments.length,
//默认不进行深度拷贝
deep = false;

// 看是不是深拷贝的情况,第一布尔值参数赋给deep
if ( typeof target === "boolean" ) {
deep = target;

// 第二个对象参数赋给target(继承对象)
target = arguments[ i ] || {};
// i 指向第三个参数
i++;
}
// 当target不是一个对象类型时(如:字符串),将target转换成空对象
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// 看是不是插件的情况
if ( i === length ) {
target = this;
i--;
}
//有多个参数时循环
for ( ; i < length; i++ ) {
// 当传入的被继承的对象是非null的时候,才进行拷贝
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// 防止循环引用
if ( target === copy ) {
continue;
}
//深拷贝,deep是true,copy非空,并且copy是一个对象或者是数组
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
//保留原有的属性,方法
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];

} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 递归调用
target[ name ] = jQuery.extend( deep, clone, copy );
// 浅拷贝
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};

欢迎关注我的其它发布渠道