JavaScript中的继续入门

正统的面相对象的言语都邑供应extend之类的要领用于出来类的继续,但Javascript并不供应extend要领,在Javascript中运用继续须要用点技能。

Javascript中的实例的属性和行动是由组织函数和原型两部分构成的,我们定义两个类:Person和zhangsan,它们在内存中的表现以下图1:
《JavaScript中的继续入门》
假如想让Zhangsan继续Person,那末我们须要把Person组织函数和原型中的属性和行动悉数传给Zhangsan的组织函数和原型,以下图2所示:
《JavaScript中的继续入门》

Are you Ok?了解了继续的思绪后,那末我们一步步完成Person和Zhangsan的继续功用。起首,我们须要定义Person类,以下代码:
[代码1]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "人";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +",我的名字叫" + this.name);
    }
}    
//定义Zhangsan类
function Zhangsan (name){
}
Zhangsan.prototype={
    
}

Zhangsan虽然有本身特有的属性和行动,但它大部分属性和行动和Person雷同,须要继续自Person类。如前所述,JavaScript中继续是要离别继续组织函数和原型中的属性和行动的。我们先让Zhangsan继续Person的组织函数中的行动和属性,以下代码:
[代码2]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
//定义Zhangsan类
function Zhangsan (name){
    this.name = name;
    this.type = "黄";
}
Zhangsan.prototype={
}
//实例化Zhangsan对象
var zs = new Zhangsan("张三");
console.info(zs.type);    // 黄

运转一般,但我们怎样没看到继续的“滋味”呢?我们在Zhangsan的组织函数中将Person的属性和行动复制了一份,与其说是继续不如说是“真巧,这两个类的组织函数除了函数名差别,其他处所都长得一样”。她的瑕玷很明显:假如Person类的组织函数有任何变动,我们也须要手动的同步修正Zhangsan类的组织函数,一样一份代码,我们复制了一份写在了顺序中 的差别处所,这违法了DRY准绳,降低了代码的可维护性。

好了,让我们来革新它:
[代码3]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    Person(name);
}
Zhangsan.prototype={
}
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
console.info(zs.type);        // undefined

我们在Zhangsan的组织函数里挪用Person()函数,愿望它内部的ths.xxx能够在Zhangsan类的组织函数里实行一遍,但奇怪的是,涌现“console.info(zs.type);”时,输出的是undefined,这是怎样回事呢?

这和Person的挪用体式格局有关。在JavaScript中,function有两种差别的挪用要领:

  1. 作为函数存在,直接用“()”挪用,比方“function test(){}; test();”test被用作函数,直接被“()”标记挪用。

  2. 作为类的组织函数存在,运用new挪用,比方“function test(){}; new test();”test作为类的组织函数,经由过程new举行test类的实例化。这两种要领的挪用,function内部的this指向会有所差别—作为函数的function,其this指向的是window,而作为组织函数的function,其this指向的实例对象。

上面代码中,Zhangsan类组织函数中的Person是经由过程函数体式格局挪用的,它内部的this指向的是window对象,起结果等同于以下代码:
[代码4]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    window.name = name;
    window.type = "黄";
}
Zhangsan.prototype={
}
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
console.info(zs.type);    // undefined
console.info(type);        // 黄 (window.type能够省略写成type)

假如想到达[代码3]的结果,让Person内部this指向Zhangsan类的实例,能够经由过程call或apply要领完成,以下:
[代码5]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    Person.call(this,name);
}
Zhangsan.prototype={
}
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
console.info(zs.type);        // 黄

组织函数的属性和行动已胜利完成了继续,接下来我们要完成原型中的属性和行动的继续。既然Zhangsan类须要和Person类原型中一样的属性和行动,那末可否将Person类的原型直接传给Zhangsan类的原型,以下代码:
[代码6]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    Person.call(this,name);
}
Zhangsan.prototype = Person.prototype;
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
// 我是一个黄种人,我的名字叫张三
zs.say();

经由过程Person类的原型传给Zhangsan类的原型,Zhangsan类胜利获得了say行动,但事变并不像设想中的那末简朴,假如我们要给Zhangsan类增加run行动呢?以下代码:
[代码7:增加run行动]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    Person.call(this,name);
}
Zhangsan.prototype = Person.prototype;
Zhangsan.prototype.run = function(){
    console.info("我100米短跑只需10秒!");
}
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
zs.say();   // 我是一个黄种人,我的名字叫张三
zs.run();   //我100米短跑只需10秒!
var zs2 = new Person("张三2");
zs2.run();    //我100米短跑只需10秒!

我们只想给Zhangsan类增加run行动,为何Person类也获得了run行动了呢?这触及传值和传址的两个题目—-在JavaScript中,赋值语句会用传值和传地点两种差别的体式格局举行赋值,假如是数值型、不尔型、字符型等基础数据类型,在举行赋值时会将数据直接赋值一份,将赋值的那一份数据举行赋值,也就是一般所说的传值;假如是数组、hash对象等庞杂数据类型,在举行赋值时会直接用内存地点赋值,而不是将数据赋值一份,这就是传址赋值,就是传数据的映照地点。
[代码8:传值与传址]

var a=10;        // 基础数据类型
var b=a;        // 将变量a保留的值赋值一份,传给变量b,b和a各保留一份数据
var c=[1,2,3];    // 庞杂数据类型
var d=c;        // 将变量c指向的数据内存地点传给变量d,c和d指向统一份数据
b++;
d.push(4);
console.info(a);    // 10
console.info(b);    // 11        变量b保留的数据变动不会影响到变量a
console.info(c);    // 1,2,3,4    变量c和d指向统一份数据,数据变动会相互影响
console.info(d);    // 1,2,3,4

在原生JavaScript中,挑选传值照样传地点是依据数据类型来自动推断的,但传地点有时候会给我们带来意想不到的贫苦,所以我们须要对庞杂数据类型的赋值举行掌握,让庞杂数据类型也能够举行传值。

最简朴的做法是遍历数组或许Hash对象,将数组或许Hash对象这类庞杂的数据拆分红一个个简朴数据,然后离别赋值,以下面代码:
[代码9:对庞杂数据类型举行传值]

var a = [1, 2, 3] ,b = {name:'张三',sex:'男',tel:'1383838438'};
var c = [] ,d = {};
for(var p in a){
    c[p] = a[p]; 
}
for(var p in b){
    d[p] = b[p];
}
c.push('4');
d.email = 'ibing@outlook.com';
console.info(a);            // [1, 2, 3]
console.info(c);            // [1, 2, 3, "4"]
console.info(b.email);        // undefined
console.info(d.email);        // ibing@outlook.com

值得一提的是,关于数组的传值还能够运用数组类的slice或许concat要领完成,以下面代码:
[代码10:数组传值的简朴要领]

var a = [1, 2, 3];
var b = a.slice(), c = a.concat();
b.pop();
c.push(4);
console.info(a);        // [1, 2, 3]
console.info(b);        // [1, 2]
console.info(c);        // [1, 2, 3, 4]

prototype本质上也是一个hash对象,所以直接用它赋值时会举行传址,这也是为何[代码7:增加润行动]中,zs2居然会run的缘由。我们能够用for in来遍历prototype,从而完成prototype的传值。但由于prototype和function(用做类的function)的关联,我们另有别的一种要领完成prototype的传值—-new SomeFunction(),以下面代码:
[代码11]

// 定义Person类
function Person (name){
    this.name = name;
    this.type = "黄";
}
Person.prototype={
    say : function(){
        console.info("我是一个"+ this.type +"种人,我的名字叫" + this.name);
    }
}    
// 定义Zhangsan类
function Zhangsan (name){
    Person.call(this,name);
}
Zhangsan.prototype = new Person();
Zhangsan.prototype.constructor = Person;
Zhangsan.prototype.run = function(){
    console.info("我100米短跑只需10秒!");
}
    
// 实例化Zhangsan对象
var zs = new Zhangsan("张三");
zs.say();   // 我是一个黄种人,我的名字叫张三
zs.run();   // 我100米短跑只需10秒!
var zs2 = new Person("张三2");
zs2.run();    // TypeError: zs2.run is not a function

您是不是注意到上面这句Zhangsan.prototype.constructor = Person;,这是由于Zhangsan.prototype = new Person();时,Zhangsan.prototype.constructor指向了Person,我们须要将它改正,从新指向Zhangsan。

    原文作者:dunizb
    原文地址: https://segmentfault.com/a/1190000004710314
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞