js知识点分享
2021-07-15 23:06
标签:回忆 功能 大量 找不到 构造 整理 任务 oat display 原型对象 在JavaScript 中,每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。注:普通对象没有prototype,但有__proto__属性。 原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。看下面的例子: function f1(){}; console.log(f1.prototype) //f1{} console.log(typeof f1. prototype) //Object console.log(typeof Function.prototype) // Function,这个特殊 console.log(typeof Object.prototype) // Object console.log(typeof Function.prototype.prototype) //undefined 从这句console.log(f1.prototype) //f1 {} 的输出就结果可以看出,f1.prototype就是f1的一个实例对象。就是在f1创建的时候,创建了一个它的实例对象并赋值给它的prototype,基本过程如下: var temp = new f1(); f1. prototype = temp; 所以,Function.prototype为什么是函数对象就迎刃而解了,上文提到凡是new Function ()产生的对象都是函数对象,所以temp1是函数对象。 var temp1 = new Function (); Function.prototype = temp1; 那原型对象是用来做什么的呢?主要作用是用于继承。举了例子: var person = function(name){ this.name = name }; person.prototype.getName = function(){ return this.name; } var zjh = new person(‘zhangjiahao’); zjh.getName(); //zhangjiahao 从这个例子可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例(例中:zjh)出来的普通对象就继承了这个属性。具体是怎么实现的继承,就要讲到下面的原型链了。 原型链 JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子为例: console.log(zjh.__proto__ === person.prototype) //true 同样,person.prototype对象也有__proto__属性,它指向创建它的函数对象(Object)的prototype console.log(person.prototype.__proto__ === Object.prototype) //true 继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null console.log(Object.prototype.__proto__) //null 我们把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。如下图: 在开始摆弄代码之前,应该搞清楚使用继承的目的和能带来什么好处。一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化类之间的耦合。而要做到这两者都兼顾是很难的,我们需要根据具体的条件和环境下决定我们应该采取什么方法。根据我们对面向对象语言中继承的了解,继承会带类直接的强耦合,但js由于其特有的灵活性,可以设计出强耦合和弱耦合,高效率和低效率的代码。而具体用什么,看情况。 下面提供js实现继承的三种方法:类式继承,原型继承,掺元类。这里先简述类式继承,后两种在往后的随便中简述,请多多关注、指导,谢谢。 类式继承。 js类式继承的实现依靠原型链来实现的。什么是原型链?js中对象有个属性prototy,这个属性返回对象类型的引用,用于提供对象的类的一组基本功能。 貌似对prototype有印象,对了,我们经常这样用代码。 1 2 3 4 5 6 7 8 9 var Person = function(){ this.name = "liyatang"; }; Person.prototype = { //可以在这里提供Person的基本功能 getName : function(){ return this.name; } } 我们把类的基本功能放在prototype属性里,表示Person这个对象的引用有XXX功能。 在理解原型后,需要理解下什么是原型链。在访问对象的某个成员(属性或方法)时,如果这个成员未见于当前对象,那么js会在prototype属性所指的那个对象中查找它,如果还没有找到,就继续到下一级的prototype所指的对象中查找,直至找到。如果没有找到就会返回undifined。 那么原型链给我们什么提示呢?很容易联想到,原型链意味着让一个类继承另一个类,只需将子类的prototype设置为指向父类的一个实例即可。这就把父类的成员绑定到子类上了,因为在子类上查找不到某个成员时会往父类中查找。(以上这两段用词不严谨,只在用通俗易懂的言语描述) 下面我们需要个Chinese类,需要继承Person类的name和getName成员。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var Chinese = function(name, nation){ //继承,需要调用父类的构造函数,可以用call调用,this指向Chinese //使Person在此作用域上,才可以调用Person的成员 Person.call(this,name); this.nation = nation; }; Chinese.prototype = Person.prototype; //这里不可和以前一样,因为覆盖掉了prototype属性 //Chinese.prototype = { // getNation : function(){ // return this.nation; // } //}; //以后的方法都需要这样加 Chinese.prototype.getNation = function(){ return this.nation; }; 继承关系就建立了,我们这样调用它 1 2 var c = new Chinese("liyatang","China"); alert(c.getName());// liyatang 于是类式继承就这样完成了。难道真的完成了嘛,用firebug在alert那里设断点,会发现原来的Person.prototype被修改了,添加了getNation方法。 1 这是因为在上面的代码Chinese.prototype = Person.prototype; 这是引用类型,修改Chinese同时也修改了Person。这本身就是不能容忍的,且使类之间形成强耦合性,这不是我们要的效果。 我们可以另起一个对象或实例化一个实例来弱化耦合性。 1 2 3 4 5 6 //第一种 //Chinese.prototype = new Person(); //第二种 //var F = function(){}; //F.prototype = Person.prototype; //Chinese.prototype = F.prototype; 这两种方法有什么区别呢。在第二种中添加了一个空函数F,这样做可以避免创建父类的一个实例,因为有可能父类会比较庞大,而且父类的构造函数会有一些副作用,或者说会执行大量的计算任务。所以力荐第二种方法。 到此,完了嘛,还没有!在对象的属性prototype下面有个属性constructor,它保存了对构造特定对象实例的函数的引用。根据这个说法Chiese.prototype.constructor应该等于Chinese,实际上不是。 回忆之前在设置Chiese的原型链时,我们把Person.prototype 覆盖掉了Chiese.prototype。所以此时的Chiese.prototype.constructor是Person。我们还需要添加以下代码 1 2 3 4 //对这里的if条件不需要细究,知道Chinese.prototype.constructor = Chinese就行 if(Chinese.prototype.constructor == Object.prototype.constructor){ Chinese.prototype.constructor = Chinese; } 整理全部代码如下 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 var Person = function(name){ this.name = name; }; Person.prototype = { getName : function(){ return this.name; } }; var Chinese = function(name, nation){ Person.call(this,name); this.nation = nation; }; var F = function(){}; F.prototype = Person.prototype; Chinese.prototype = F.prototype; if(Chinese.prototype.constructor == Object.prototype.constructor){ Chinese.prototype.constructor = Chinese; } Chinese.prototype.getNation = function(){ return this.nation; }; var c = new Chinese("liyatang","China"); alert(c.getName()); 如果可以把继承的代码放在一个函数里,方便代码复用,最后整理代码如下 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 function extend(subClass,superClass){ var F = function(){}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; //加多了个属性指向父类本身以便调用父类函数 if(superClass.prototype.constructor == Object.prototype.constructor){ superClass.prototype.constructor = superClass; } } var Person = function(name){ this.name = name; }; Person.prototype = { getName : function(){ return this.name; } }; var Chinese = function(name, nation){ Person.call(this,name); this.nation = nation; }; extend(Chinese, Person); Chinese.prototype.getNation = function(){ return this.nation; }; var c = new Chinese("liyatang","China"); alert(c.getName()); 发表后修改: 在一楼的评论下,我对那个extend函数又有新的看法。之前在讨论如何设置原型链时提出了两种方法 1 2 3 4 5 6 //第一种 //Chinese.prototype = new Person(); //第二种 //var F = function(){}; //F.prototype = Person.prototype; //Chinese.prototype = F.prototype; 虽然第二种减少了调用父类的构造函数这条路,但在设计Chinese类时用了Person.call(this,name);这里也相当于调用了父类的构造函数。 然而用第一种方法的话可以减少在Chinese中再写Person.call(this,name);,这部分代码在子类中往往会遗忘。不妨把这种功能代码放在了extend里。就只写 Chinese.prototype = new Person();也达到同样的目的:耦合不强。 但遗忘的一点是,Chinese.prototype = new Person();这样写对嘛。答案是不对!很明显 new Person()需要传一个name参数的。我们不可能在extend函数里做这部分工作,只好在Chinese类里调用父类的构造函数了。这样也符合面向对象的思路。 在开始摆弄代码之前,应该搞清楚使用继承的目的和能带来什么好处。一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化类之间的耦合。而要做到这两者都兼顾是很难的,我们需要根据具体的条件和环境下决定我们应该采取什么方法。根据我们对面向对象语言中继承的了解,继承会带类直接的强耦合,但js由于其特有的灵活性,可以设计出强耦合和弱耦合,高效率和低效率的代码。而具体用什么,看情况。 下面提供js实现继承的三种方法:类式继承,原型继承,掺元类。这里先简述类式继承,后两种在往后的随便中简述,请多多关注、指导,谢谢。 类式继承。 js类式继承的实现依靠原型链来实现的。什么是原型链?js中对象有个属性prototy,这个属性返回对象类型的引用,用于提供对象的类的一组基本功能。 貌似对prototype有印象,对了,我们经常这样用代码。 我们把类的基本功能放在prototype属性里,表示Person这个对象的引用有XXX功能。 在理解原型后,需要理解下什么是原型链。在访问对象的某个成员(属性或方法)时,如果这个成员未见于当前对象,那么js会在prototype属性所指的那个对象中查找它,如果还没有找到,就继续到下一级的prototype所指的对象中查找,直至找到。如果没有找到就会返回undifined。 那么原型链给我们什么提示呢?很容易联想到,原型链意味着让一个类继承另一个类,只需将子类的prototype设置为指向父类的一个实例即可。这就把父类的成员绑定到子类上了,因为在子类上查找不到某个成员时会往父类中查找。(以上这两段用词不严谨,只在用通俗易懂的言语描述) 下面我们需要个Chinese类,需要继承Person类的name和getName成员。 继承关系就建立了,我们这样调用它 于是类式继承就这样完成了。难道真的完成了嘛,用firebug在alert那里设断点,会发现原来的Person.prototype被修改了,添加了getNation方法。 这是因为在上面的代码Chinese.prototype = Person.prototype; 这是引用类型,修改Chinese同时也修改了Person。这本身就是不能容忍的,且使类之间形成强耦合性,这不是我们要的效果。 我们可以另起一个对象或实例化一个实例来弱化耦合性。 这两种方法有什么区别呢。在第二种中添加了一个空函数F,这样做可以避免创建父类的一个实例,因为有可能父类会比较庞大,而且父类的构造函数会有一些副作用,或者说会执行大量的计算任务。所以力荐第二种方法。 到此,完了嘛,还没有!在对象的属性prototype下面有个属性constructor,它保存了对构造特定对象实例的函数的引用。根据这个说法Chiese.prototype.constructor应该等于Chinese,实际上不是。 回忆之前在设置Chiese的原型链时,我们把Person.prototype 覆盖掉了Chiese.prototype。所以此时的Chiese.prototype.constructor是Person。我们还需要添加以下代码 整理全部代码如下 如果可以把继承的代码放在一个函数里,方便代码复用,最后整理代码如下 发表后修改: 在一楼的评论下,我对那个extend函数又有新的看法。之前在讨论如何设置原型链时提出了两种方法 虽然第二种减少了调用父类的构造函数这条路,但在设计Chinese类时用了Person.call(this,name);这里也相当于调用了父类的构造函数。 然而用第一种方法的话可以减少在Chinese中再写Person.call(this,name);,这部分代码在子类中往往会遗忘。不妨把这种功能代码放在了extend里。就只写 Chinese.prototype = new Person();也达到同样的目的:耦合不强。 但遗忘的一点是,Chinese.prototype = new Person();这样写对嘛。答案是不对!很明显 new Person()需要传一个name参数的。我们不可能在extend函数里做这部分工作,只好在Chinese类里调用父类的构造函数了。这样也符合面向对象的思路。 js知识点分享 标签:回忆 功能 大量 找不到 构造 整理 任务 oat display 原文地址:http://www.cnblogs.com/zxlasd/p/7071893.html
var
Person =
function
(){
this
.name =
"liyatang"
;
};
Person.prototype = {
//可以在这里提供Person的基本功能
getName :
function
(){
return
this
.name;
}
}
var
Chinese =
function
(name, nation){
//继承,需要调用父类的构造函数,可以用call调用,this指向Chinese
//使Person在此作用域上,才可以调用Person的成员
Person.call(
this
,name);
this
.nation = nation;
};
Chinese.prototype = Person.prototype;
//这里不可和以前一样,因为覆盖掉了prototype属性
//Chinese.prototype = {
// getNation : function(){
// return this.nation;
// }
//};
//以后的方法都需要这样加
Chinese.prototype.getNation =
function
(){
return
this
.nation;
};
var
c =
new
Chinese(
"liyatang"
,
"China"
);
alert(c.getName());
// liyatang
//第一种
//Chinese.prototype = new Person();
//第二种
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;
//对这里的if条件不需要细究,知道Chinese.prototype.constructor = Chinese就行
if
(Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
}
var
Person =
function
(name){
this
.name = name;
};
Person.prototype = {
getName :
function
(){
return
this
.name;
}
};
var
Chinese =
function
(name, nation){
Person.call(
this
,name);
this
.nation = nation;
};
var
F =
function
(){};
F.prototype = Person.prototype;
Chinese.prototype = F.prototype;
if
(Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
}
Chinese.prototype.getNation =
function
(){
return
this
.nation;
};
var
c =
new
Chinese(
"liyatang"
,
"China"
);
alert(c.getName());
function
extend(subClass,superClass){
var
F =
function
(){};
F.prototype = superClass.prototype;
subClass.prototype =
new
F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype;
//加多了个属性指向父类本身以便调用父类函数
if
(superClass.prototype.constructor == Object.prototype.constructor){
superClass.prototype.constructor = superClass;
}
}
var
Person =
function
(name){
this
.name = name;
};
Person.prototype = {
getName :
function
(){
return
this
.name;
}
};
var
Chinese =
function
(name, nation){
Person.call(
this
,name);
this
.nation = nation;
};
extend(Chinese, Person);
Chinese.prototype.getNation =
function
(){
return
this
.nation;
};
var
c =
new
Chinese(
"liyatang"
,
"China"
);
alert(c.getName());
//第一种
//Chinese.prototype = new Person();
//第二种
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;