如何理解javascript的原型
每一个javascript对象在创建时就会与之关联另一个对象,之歌对象就是我们所说的原型,每个对象都会从原型继承属性
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性。
- 所有的引用类型(数组、对象、函数),都有一个proto属性,属性值是一个普通的对象
- 所有的函数,都有一个prototype属性,属性值也是一个普通对象。
- 所有的引用类型(数组、对象、函数),proto属性值指向它的构造函数的prototype属性值。
//自由扩展属性
var obj = {};
obj.a = 100;
var arr = [];
arr.a = 100;
function fn() {}
fn.a = 100
//_proto_
console.log(obj._proto_);
console.log(arr.__proto__);
console.log(fn.__proto__);
// 要点三:函数有 prototype
console.log(fn.prototype)
// 要点四:引用类型的 __proto__ 属性值指向它的构造函数的 prototype 属性值
console.log(obj.__proto__ === Object.prototype)
如何理解javascript原型链
如果我们要执行fn.toString(),首先会去fn的原型上找这个方法,然后发现没有这个方法,那就去它的构造函数的原型上找这个方法,因为它直接继承于Object,而在Ojbect的原型上找到了这个方法。
像这样一直往上找的过程,是一个链式的结构,我们把它称做原型链。如果一直想上找到最上层都没有找到,那么就宣布失败,返回undefined。最上层是
Object.prototype._proto_ === null
如何实现继承
提到了原型链,那面试官一般会提到的下一个问题就是继承。
我们知道所有的函数继承至Object,那我们当然也可以运用原型链的原理,让一个函数继承另一个函数。
function People() {
}
People.prototype.console = ()=>{
console.log('this is a people')
}
function Person() {}
Person.prototype = new People()
Person.prototype.alert = ()=> {
alert('thia is a person')
}
var hyp = new Person()
hyp.console();//this a people
hyp.alert();//this is a person
我们通过原型实现了简单的继承,但是这样的继承有一些缺点。我们看下面代码
function People(name) {
this.friendArr = []
}
People.prototype.friend = function(){
this.friendArr.push(fn,name)
}
function Person1() {}
function Person2() {}
Person1.prototype = new People()
Person2.prototype = new People()
var hyp = new Person1()
var xhy = new Person2()
hyp.friend(hyp,'xhy');
hyp.friendArr//['xhy']
xhy.friend('hyp');
xhy.friendArr//['xhy','hyp']
因为原型的共享性,所以它的所以实例都可以对它进行修改,并且这个修改会被同步到所有实例。为了解决这个问题,我们可以借用构造函数
function People() {
this.friendArr = []
}
function Person1() {
People.call(this)
}
function Person2() {
People.call(this)
}
var hyp = new Person1()
var xhy = new Person2()
hyp.friendArr.push('xhy')
xhy.friendArr.push('hyp')
console.log(hyp.friendArr,xhy.friendArr)//'xhy' 'hyp'
借用构造函数的问题
如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定 义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结 果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。
组合继承
在这里我们来想一下原始继承和构造函数继承各自的缺点
- 原型继承导致属性共享
- 构造函数继承导致方法多次被重写
于是我们用构造函数的方式来继承属性,用原型的方式来继承方法不就好了吗
function People(name) {
this.name = name;
this.color = ['w','r'];
}
People.prototype.sayName =()=>{
console.log(
this.name)
}
function Person1() {
People.call(this)
}
Person1.prototype = new People()
function Person2() {
People.call(this)
}
Person2.prototype = new People()
var hyp = new Person1(' hyp')
var xhy = new Person2('xhy')
hyp.sayName()//hyp
xhy.sayName()//xhy