JavaScript原型、原型链、对象的创建

JavaScript原型

在JavaScript中,每个函数
都有一个prototype属性,当一个函数被用作构造函数来创建实例时,这个函数的prototype属性值会被作为原型赋值给所有对象实例。
所有函数对象的_proto_最终都指向Function.prototype,它是一个空函数.

代码:

function Son (name) { this.name = name; }
function Mother () { }

//Mother的原型
Mother.prototype = { 
    age: 18,
    home: ['Beijing', 'Shanghai']
};
//Son的原型设为Mother
Son.prototype = new Mother(); 

var p1 = new Son('小明'); //p1:'小明'; __proto__:{__proto__:18,['Beijing','Shanghai']}
var p2 = new Son('蛋蛋'); //p2:'蛋蛋'; __proto__:{__proto__:18,['Beijing','Shanghai']}

//实例不会改变原型的基本值属性
p1.age = 20;      //p1:'小明',20; __proto__:{__proto__:18,['Shenzhen','Shanghai']}

//改变了原型Mother中的属性
// p1:'小明',20; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
// p2:'蛋蛋';    __proto__:{__proto__:18,['Shenzhen','Shanghai']}
p1.home[0] = 'Shenzhen'; 


//可以理解为p1.age=20
p1.home = ['Hangzhou', 'Guangzhou'];  //p1:'小明',20,['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}

//删除实例的属性之后,将会显示原本被覆盖的原型值,这就是向上的搜索机制
delete p1.age;    //p1:'小明',['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}

//为Son的原型Monther增加属性(改写原型,动态反应到实例中)
// p1:'小明',['Hangzhou','Guangzhou']; __proto__:{'Li',__proto__:18,['Shenzhen','Shanghai']}
// p2:'蛋蛋';                          __proto__:{'Li',__proto__:18,['Shenzhen','Shanghai']}
Son.prototype.lastName = 'Li'; 

//更换Son的原型,就好像换了一个后妈一样
Son.prototype = { 
    age: 20, 
    address: { city: 'Ji nan' }
};

// p1:'小明',['Hangzhou','Guangzhou']; __proto__:{'Li',__proto__:18,['Shenzhen','Shanghai']}
// p2:'蛋蛋';                          __proto__:{'Li',__proto__:18,['Shenzhen','Shanghai']}
// p3:'小花';__proto__: 20 {city: 'Ji nan'}
var p3 = new Son('小花'); 

//为Mother的原型增加属性,即为姥姥增加了属性
//但是上面因为更换了son的原型,所以Mother的变换不会影响p3
// p1:'小明',['Hangzhou','Guangzhou']; __proto__:{'Li',__proto__:18,['Shenzhen','Shanghai'],233}
// p2:'蛋蛋';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai'],233}
// p3: '小花';__proto__: 20 {city: 'Ji nan'}
Mother.prototype.adressnum = 233;

//更换Mother的原型,即更换了姥姥
//但是因为上面son的原型已经不是mother了,所以Mother怎么变不会影响Son。
Mother.prototype = { 
    car: 2, 
    hobby: ['run','walk']
};

var p4 = new Son('Tony');  //p4:'Tony';__proto__: 20 {city: 'Ji nan'}

//想让son应用这些改变的话,需要重新绑定mother
Son.prototype = new Mother(); //再次绑定

var p5 = new Son('小硕'); // p5:'小硕';__proto__:{__proto__: 2, ['run','walk']}

对于p1.home[0] = ‘Shenzhen’; 为何mother、p1和p2都受影响呢?

这是因为:p1中并不存在home这个数组。当你使用p1.home[0]时,在本地找不到home这个变量,这时由于原型链的向上搜索机制,会到p1的原型mother中寻找,找不到,接着到mother的原型中寻找,这里找到了home这个属性,于是就改变了mother原型的home[0]属性。
因此p1.home[0] = ‘Shenzhen’ 就等同于 Mother.prototype.home[0] = ‘Shenzhen’

原型链

原型链是实现继承的主要方法。
原型链的终点为null

原理:利用原型让一个引用类型继承另一个引用类型的属性和方法。
核心:属性共享和独立的控制

原型链

原型链继承的主要问题在于属性的共享:

组合继承
function Mother (age) {
    this.age = age;
    this.hobby = ['running','football']
}
Mother.prototype.showAge = function () {
    console.log(this.age); 
};

function Person (name, age) { 
    Mother.call(this, age);  
    this.name = name; 
}
Person.prototype = new Mother();  //第一次执行mother
Person.prototype.constructor = Person;
Person.prototype.showName = function () {
    console.log(this.name);
}

var p1 = new Person('Jack', 20); //第二次执行mother
p1.hobby.push('basketball');  //p1:'Jack'; __proto__:20,['running','football','basketball']
var p2 = new Person('Mark', 18);  //p2:'Mark'; __proto__:18,['running','football']

通过第二次执行原型的构造函数 Mother(),在对象实例中复制了一份原型的属性,这样就做到了与原型属性的分离独立。但是第一次调用 Mother(),好像什么用都没有,能不调用他吗?当然可以,于是就有了下面的寄生组合式继承。

寄生组合式继承
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
//避免了new Mother(),所以没了第一次执行
function inheritPrototype(Person, Mother){
    var prototype = object(Mother.prototype); 
    prototype.constructor = Person;    
    Person.prototype = prototype;    
}

function Mother (age) {
    this.age = age;
    this.hobby = ['running','football']
}
Mother.prototype.showAge = function () {
    console.log(this.age); 
};

function Person (name, age) { 
    Mother.call(this, age);
    this.name = name; 
}

inheritPrototype(Person, Mother);

Person.prototype.showName = function () {
    console.log(this.name);
}

var p1 = new Person('Jack', 20); 
p1.hobby.push('basketball');//p1:'Jack'; __proto__:20,['running','football','basketball']
var p2 = new Person('Mark', 18); //p2:'Mark'; __proto__:18,['running','football']

关键点在于 object(o) 里面,这里借用了一个临时对象来巧妙避免了调用new Mother(),然后将原型为 o 的新对象实例返回,从而完成了原型链的设置。

Js创建对象的方法

//最原始模式,对象字面量方式
var person = { 
    name: 'Sun Miao',
    age: 22,
    sayName: function () { alert(this.name); }
};

最原始的模式不适合批量创建对象,批量创建可以考虑工厂模式

//工厂模式,定义一个函数创建对象
function creatPerson (name, age) {
    var person = new Object(); 
    person.name = name;
    person.age = age;
    person.sayName = function () {
        alert(this.name);
    };
    return person; 
}

工厂模式缺点是每次创建对象都会产生一个临时对象,而且你无法缺点创建的对象具体是什么类型,因为new Object().
这是可以考虑构造函数模式。

//构造函数模式,定义一个构造函数
function Person (name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        alert(this.name);
    };    
}
var p1 = new Person('Sun Miao', 22);

通过构造函数创建的对象,其中的方法(这里把方法看成是和name一样的变量就好)也都是各自独立的,比如sayName方法,如果我们想让多个对象共用一个sayName怎么办?
那就得用到原型模式了。

//原型模式1,直接定义prototype属性方式
function Person () {}
Person.prototype.name = 'Sun Miao';
Person.prototype.age = 22;
Person.prototype.sayName = function () { alert(this.name); };

//原型模式2,字面量定义方式
function Person () {}
Person.prototype = {
    name: 'Sun Miao',
    age: 22,
    sayName: function () { alert(this.name); }
};
var p1 = new Person(); 

需要注意的是原型属性和方法的共享,即所有实例中都只是引用原型中的属性方法,任何一个地方产生的改动会引起其他实例的变化。

但我们只想共享某些方法,其他变量仍保持独立,那怎么办?
那就把原型和构造组合起来呗~

//原型+构造 模式
//对于要求独立的变量,使用构造模式
function Person (name, age) {
    this.name = name;
    this.age = age;
};
//对于要求共享的变量,使用原型模式
Person.prototype = {
    hobby: ['warking','football'];
    sayName: function () { alert(this.name); },
    sayAge: function () { alert(this.age); }
};

var p1 = new Person('Sun Miao', 22); 
//p1:'Sun Miao',22; __proto__: ['warking','football'],sayName,sayAge

var p2 = new Person('Wen Shuo', 23); 
//p1:'Wen Shuo',23;__proto__: ['warking','football'],sayName,sayAge

这样做既节省了内存开销又保留了对象实例的独立性,一举两得。

参考文章

非常感谢茄果写的这篇关于原型理解的文章,看了之后很多模糊的地方都通了,例子比喻的很形象,通俗易懂。不像某些文章扯一堆官话,让人看一眼就没看下去的欲望了O(∩_∩)O哈哈~
http://www.cnblogs.com/qieguo/p/5451626.html
还有它写的关于闭包的文章也很出色,绝对是最好理解的文章。没有之一:
http://www.cnblogs.com/qieguo/p/5457040.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值