//1.定义一个构造函数
function Obj(a) {
this.a = a || 'a';
}
console.dir(Obj.prototype.constructor);// Obj(函数自身)
//构造函数的prototype.constructor应指向函数自身
//2.在该构造函数的原型中 定义该构造函数创建的所有对象都会继承的公共方法
Obj.prototype.getA = function() {
return this.a;
}
var o1 = new Obj('1');
console.log(o1.getA()) // '1'
function obj(a, b) {
//通过改变父类构造函数的执行上下文,继承父类构造函数中定义的属性
Obj.call(this, a);
this.b = b;
}
var o2 = new obj('2', ['a','b']);
console.log(o2.getA()); // 报错
由于getA方法被定义在Obj.prototype中,call方法只能使obj调用Obj内部定义的方法,故Obj.prototype中定义的方法没有被obj继承。要解决此问题可以将getA定义在Obj对象内部,但是这样会在每一个实例中创建一个getA方法,对于公用函数来说这样做显然会造成资源浪费,将公共方法定义在构造函数的prototype原型中来继承则更加适合。
//通过将prototype指向父级创建的实例,继承父类构造函数prototype中定义的方法
obj.prototype = new Obj(); //此时obj.prototype.constructor 为 Obj
obj.prototype.constructor = obj; //修正constructor指向为obj函数自身,避免修改原型链时造成的constructor丢失?
var o3 = new obj('3', ['a','b']);
console.log(o2.getA()); //'3'
注意 obj.prototype = new Obj()与obj.prototype = Obj.prototype的区别:
前者将obj的prototype指向了Obj创建的一个实例,该实例的__proto__指向Obj.prototype(通过new创建的对象,其__proto__总是指向其构造函数的prototype)
后者将obj和Obj的prototype指向同一个原型对象,如果定义子构造函数的prototype中的属性或方法将影响到父级。
构造函数在实例化对象时,每一个子类都将调用一次父类构造函数。
上面代码中
obj.prototype = new Obj();
可写作
obj.prototype = (function() {
function O() {}
O.prototype = Obj.prototype;
return new O();
})();
封装成继承函数
//继承函数
function extend(son, father) {
function F() {}
F.prototype = father.prototype;
son.prototype = new F();
son.prototype.constructor = son;
}
这样在创建obj实例时,减少了对父类的构造函数的调用次数,在继承层级较多时,可以减少内存占用。
使用这种继承方式时要尽量减少继承层级,参考下面例子,创建一个E对象实例共调用5次父级构造函数。
//A类
function A() {
console.log('A()');
}
A.prototype.print = function() {
console.log('msg');
}
//B类
function B() {
A.call(this);
console.log('B()');
}
extend(B, A);
//C类
function C() {
B.call(this);
console.log('C()');
}
extend(C, B);
//D类
function D() {
C.call(this);
console.log('D()');
}
extend(D, C);
//E类
function E() {
D.call(this);
console.log('E()');
}
extend(E, D);
//创建一个E的实例
var e = new E(); //A() B() C() D() E()
e.print(); //msg.
在前面的继承中,是将子构造函数的prototype指向父构造函数的实例,下面的代码会将一个构造函数的prototype指向一个对象,并用该构造函数创建实例。
//继承函数
function clone(object) {
function F() {}
F.prototype = object;//指定一个对象
return new F();
}
//父级
var Obj1 = {
a: 'a',
getA: function() {
return this.a;
}
}
//子级
var Obj2 = clone(Obj1);
Obj2.b = ['b'];
Obj2.getB = function() {
return this.b.join(',');
}
//创建实例1
var obj1 = clone(Obj2);
obj1.b.push('b1');
//创建实例2
var obj2 = clone(Obj2);
obj2.b.push('b2');
console.log(obj1);//b,b1,b2
console.log(obj2);//b,b1,b2
console.log(Obj2);//b,b1,b2
使用这种继承方式时,创建的所有实例虽然会返回一个新对象,但其引用和父级指向同一个内存地址,只有重新赋值时才会分配新的内存地址,如果直接操作其修改会影响到父级。
//创建实例1
var obj1 = clone(Obj2);
obj1.b = [];
obj1.b.push('b1');
//创建实例2
var obj2 = clone(Obj2);
obj2.b = [];
obj2.b.push('b2');
console.log(Obj2);//b
console.log(obj1);//b1
console.log(obj2);//b2
new关键字只能对(构造)函数使用,如果要继承的目标是一个对象需要通过设置prototype来实现,而且在存在操作实例时影响父级的风险。使用Object.create()方法可以以一个对象为目标来创建一个新对象,并实现继承。
var obj = {
name:'name',
printName:function(){
console.log(this.name)
}
}
//var obj2 = new obj1();//报错,
//new关键字只能用function(构造函数)来new对象,不能直接用对象来new对象
var obj1 = Object.create(obj);
obj1.name1 = 'name1';
console.dir(obj1);//{name1:'name1'},obj1内部没有name属性,只有name1属性
obj1.printName();//name,obj继承并能够调用printName方法和读取name属性
console.dir(obj2.prototype);//undefined
//所有索引对象(Array Object Function)都有__proto__,只有function对象才有prototype
console.dir(obj2.__proto__);//obj对象{name:'name',printName:function(){console.log(this.name)}}
//Object.create()方法创建的对象其__proto__原型指向创建的目标对象
使用Object.create()方法可以实现多重继承。
var obj = {
name:'name',
print:function(key){
console.log(this[key])
}
}
var obj1 = Object.create(obj);
obj1.name1 = 'name1';
var obj2 = Object.create(obj1);
obj2.name2 = 'name2';
obj2.print('name');//name
obj2.print('name1');//name1
obj2.print('name2');//name2
console.dir(obj2);//{name2:'name2'}
//obj2内部没有name属性,只有name2属性,但继承了obj1和obj的属性和方法
console.dir(obj2.__proto__);//obj1对象
console.dir(obj2.__proto__.__proto__);//obj对象
console.dir(obj2.__proto__.__proto__.__proto__);//Object对象
console.dir(obj2.__proto__.__proto__.__proto__.__proto__);//null
//所有对象的__proto__原型最终都会指向Object对象,Object对象原型为null
由于Object.create()创建的对象其__proto__直接其指向父级,所以父对象修改会影响子对象,而子对象修改无法影响父对象。
注意 Object.create方法不管参数是对象还是构造函数,都只能创建出对象。
如果创建的目标是函数,则创建结果是__proto__指向目标函数的空对象({ } ,不会报错)
在前面已经多次涉及原型链知识,再提一下
var func0 = function(){
this.name0 = 'func0';
}
func0.prototype.print0 = function(){
console.log(this.name0);
}
console.dir(func0.prototype); //{print0:function(){console.log(this.name0)}}原型对象
console.dir(func0.prototype.constructor);//func0
//函数的prototype.constructor默认指向该函数自身
var func1 = function (){
this.name1 = 'func1'
func0.call(this);
}
func1.prototype = new func0();
func1.prototype.print1 = function(){
console.log(this.name1);
}
console.dir(func1.prototype); //func0实例对象
console.dir(func1.prototype.constructor);//func0 由于将原型指向了func0实例,导致构造器指向func0
func1.prototype.constructor = func1;
//手动将构造器指向修正回函数自身,可以确保由fun1创建的实例instanceof为fun1,避免造成instanceof失真
console.dir(func1.prototype.__proto__);//{print0:function(){console.log(this.name0)}}原型对象
//由于func1.prototype为func0实例,故func1.prototype.__proto__指向func0.prototype
//通过new创建的对象,其__proto__总是指向其构造函数的prototype
console.dir(func1.__proto__);//Function对象
//func1是通过new Function()创建实例,Function构造函数对象的prototype是Function对象
console.dir(func1.__proto__.__proto__);//Object对象
//所有引用类型数据原型都是原自Object对象
//Function对象的构造函数的prototype是Object对象
console.dir(func1.__proto__.__proto__.__proto__);//null
//已经查找到原型链终点,即Object对象的__proto__为null
JS中所有索引对象(Array Object Function)都有__proto__属性,但是只有function对象才有prototype属性。
JS中不论通过 var method = function(){},var method = new Function(),function method(){} 三种方法中哪一种创建的函数,都会带有prototype属性,该属性默认包含__proto__属性和constructor属性,函数prototype.constructor默认指向函数自身。
JS中索引对象的__proto__属性无法直接访问,需要通过Object.getPrototypeOf()访问(即Object.getPrototypeOf( obj ) === obj.__proto__)。通过new创建的对象,其__proto__指向其构造函数的prototype(即obj.constructor.prototype === obj.__proto__)。通过Object.create()创建的对象,其__proto__指向其父级对象(即Object.create(obj).__proto__ === obj)。
如果一个对象是Function类型的,那么本身包含的prototype和__proto__是完全不同的两个概念。__proto__对应其constructor.prototype,constructor为Function构造函数;prototype则默认是一个只有constructor属性的对象,prototype.constructor对应其自身。
var func = function(){}
console.log(func.prototype) // {constructor : func 构造函数} 对象
console.log(func.prototype.constructor == func) //函数自身
console.log(func.__proto__ == func.constructor.prototype) // 一个匿名函数function(){}
console.log(Fucntion == func.constructor) // Fucntion构造函数
console.log(Fucntion.__proto__== Fucntion.prototype ) // 一个匿名函数function(){}
console.log(Fucntion.__proto__.__proto__) // Object对象
console.log(Fucntion.__proto__.__proto__.__proto__) // null
var newObj = new func()
console.dir(newObj.__proto__) // {constructor : func 构造函数} 对象
console.dir(newObj.__proto__.__proto__) // Object对象
console.dir(newObj.__proto__.__proto__.__proto__) // null
var obj = {}
console.dir(obj.__proto__) // Object对象
console.dir(obj.__proto__.__proto__) // null
var arr = []
console.dir(arr.__proto__) // Array对象
console.dir(arr.__proto__.__proto__) // Object对象
console.dir(arr.__proto__.__proto__.__proto__) // null
图片转载自http://www.mollypages.org/misc/js.mp