JS深入浅出(三)

原创
2016/03/11 18:20
阅读数 113

七、oop

JS解释器按照如下顺序找到我们定义的函数和变量:

1.函数参数(若未传入,初始化该参数为undefined)
2.函数声明(若发生命名冲突,会覆盖)——函数声明提升的原因
3.变量声明(初始化变量值undefined,若发生命名冲突,会忽略)
在全局作用域下,函数声明和变量声明会被前置到全局执行上下文(执行环境)中。
在浏览器环境下,当this表示全局对象时,this就指window对象
匿名函数,加上括号就变成了函数表达式,再加个括号,就变成了立即执行函数,再在前面加个!号可以将函数声明变为函数表达式,防止函数被前置执行,留下最后那个括号...
同一个函数,被调用多次的话,每次调用函数时都会有独立的执行上下文,每个执行上下文环境都会记录各自的变量参数等信息。

继承

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

 实现原型链有一种基本模式,其代码大致如下。 

function SuperType(){ 

        this.property = true;

}

SuperType.prototype.getSuperValue = function(){ 

          return this.property; 

}; 

function SubType(){

        this.subproperty = false; 

//继承了SuperType 

SubType.prototype = new SuperType(); 

SubType.prototype.getSubValue = function (){ 

               return this.subproperty;

 }; 

var instance = new SubType(); 

alert(instance.getSuperValue()); //true 

借用构造函数

在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术(有时候也叫做伪造对象或经典继承)。这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。如下所示: 

function SuperType(){ 

              this.colors = ["red", "blue", "green"]; 

function SubType(){ 

 //继承了 SuperType 

 SuperType.call(this); 

var instance1 = new SubType(); 

instance1.colors.push("black"); 

alert(instance1.colors); //"red,blue,green,black" 

var instance2 = new SubType(); 

alert(instance2.colors); //"red,blue,green" 

组合继承

既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。下面来看一个例子。 

function SuperType(name){ 

              this.name = name; 

              this.colors = ["red", "blue", "green"]; 

SuperType.prototype.sayName = function(){ 

        alert(this.name); 

}; 

function SubType(name, age){ 

 //继承属性 

 SuperType.call(this, name); 

 this.age = age; 

}

 //继承方法 

SubType.prototype = new SuperType(); 

SubType.prototype.constructor = SubType; 

SubType.prototype.sayAge = function(){ 

               alert(this.age); 

}; 

var instance1 = new SubType("Nicholas", 29); 

instance1.colors.push("black"); 

alert(instance1.colors); //"red,blue,green,black" 

instance1.sayName(); //"Nicholas"; 

instance1.sayAge(); //29 

var instance2 = new SubType("Greg", 27); 

alert(instance2.colors); //"red,blue,green" 

instance2.sayName(); //"Greg"; 

instance2.sayAge(); //27 

原型式继承

var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

var anotherPerson = object(person); 

anotherPerson.name = "Greg"; 

anotherPerson.friends.push("Rob"); 

var yetAnotherPerson = object(person); 

yetAnotherPerson.name = "Linda"; 

yetAnotherPerson.friends.push("Barbie"); 

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

克罗克福德主张的这种原型式继承,要求你必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后再根据具体需求对得到的对象加以修改即可。在这个例子中,可以作为另一个对象基础的是person对象,于是我们把它传入到object()函数中,然后该函数就会返回一个新对象。这个新对象将person作为原型,所以它的原型中就包含一个基本类型值属性和一个引用类型值属性。这意味着person.friends不仅属于person所有,而且也会被anotherPerson以及yetAnotherPerson 共享。实际上,这就相当于又创建了person对象的两个副本。 

ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同。

var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

var anotherPerson = Object.create(person); 

anotherPerson.name = "Greg"; 

anotherPerson.friends.push("Rob"); 

var yetAnotherPerson = Object.create(person); 

yetAnotherPerson.name = "Linda"; 

yetAnotherPerson.friends.push("Barbie"); 

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 

Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。

寄生式继承

即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。以下代码示范了寄生式继承模式。 

function createAnother(original){ 

              var clone = object(original); //通过调用函数创建一个新对象 

              clone.sayHi = function(){ //以某种方式来增强这个对象 

                        alert("hi"); 

              }; 

     return clone; 

 } / /返回这个对象 

在这个例子中,createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给object()函数,将返回的结果赋值给clone。再为clone对象添加一个新方法sayHi(),最后返回clone对象。

可以像下面这样来使用createAnother()函数: 

var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; 

var anotherPerson = createAnother(person); 

anotherPerson.sayHi(); //"hi" 

这个例子中的代码基于person返回了一个新对象——anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。

寄生组合式继承

寄生组合式继承的基本模式如下所示。 

function inheritPrototype(subType, superType){ 

              var prototype = object(superType.prototype); //创建对象

              prototype.constructor = subType; //增强对象

              subType.prototype = prototype; //指定对象

 } 

这个示例中的 inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加constructor属性,从而弥补因重写原型而失去的默认的constructor属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用 inherit- Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了。

function SuperType(name){ 

              this.name = name;

              this.colors = ["red", "blue", "green"]; 

SuperType.prototype.sayName = function(){ 

                  alert(this.name); 

}; 

function SubType(name, age){ 

              SuperType.call(this, name); 

              this.age = age; 

inheritPrototype(SubType, SuperType); 

SubType.prototype.sayAge = function(){ 

               alert(this.age); 

}; 

欢迎关注我的个人订阅号:前端生活


展开阅读全文
打赏
2
0 收藏
分享
加载中
有毅力!
2016/03/13 13:58
回复
举报
更多评论
打赏
1 评论
0 收藏
2
分享
返回顶部
顶部