What we Think , latest news

关于面向对象的总结和疑惑(转载可乐冰

在这节,老师讲了面向对象的三大特性:1、全局变量;2、封装;3、继承;

现在我就我自己的理解总结一下这节课的内容,并提出相应的疑惑,望老师解答

 

其一:全局变量

声明变量的方法有三:1,在全局对象中var a一个变量;2,window.a一个变量;3.直接去掉var,如:a=1一个变量。

第一种声明方式是我们声明变量的标准形式。现在我主要说说第二种和第三种声明变量的方式:

对于第二种方式和第三种方式声明的变量,不同于标准声明1:

其实实质上就是创建了全局对象的一个新属性,本质上不是变量。并且这两种方式声明的变量只在代码执行阶段才会出现,在预解析代码时是不会出现在执行环境中(变量对象)中。

对于第二种方式和第三种方式声明的变量,不同于标准声明2:

这两种声明变量的方式是可以运用delete操作符删除的,而标准声明方式却不能,原因在于标准声明的变量有一个Donot delete属性。(也除例外情况,如:在eval上下文中,标准声明的方式没有Donot delete这个属性,所以也是可以删除的)

注:例外在声明变量时应少声明全局变量,会引发一些不必要的影响

 

其二:封装

说到封装,先讲讲三大声明的形式:1、private;2、protected;3、public

js中并没有这些特性,但我们可以通过代码人为实现这三种特性

对于私有类型(private),我们便可以采用封装的形式,将函数中一部分信息隐藏,但为了给外部引用,提供相应的接口(我觉得这就是引用闭包的特性,来实现这些接口)

对于保护类型(protected),虽然没有其实现方法,但我们可以通过人为的书写规范来辨别,如:将受保护类型的变量,可以在变量名前加下划线来区分它与公有类型。

 

其三:继承(困扰我好久)

其实,老师上课说的继承的两种实现方式,只说了其实现过程,并没有具体和我们分析分析,所以我就找了好多资料,来做一个总结吧!

先说说普通的原型链继承:

function sub(){

    this.proterty=true;

}

sup.prototype.getValue=function(){

    return this.property;

}

function sub(){

    this.subValue=false;

}

sub.prototype=new sup();

var instance=new sub();

alert(instance.getValue());

原型链继承:

原理:

就是通过原型链来实现的继承,就是你的sub函数的原型对象的_proto_属性指向sup的原型对象,而sub实例化的instance对象的_proto_属性指向sub的原型对象,通过这条链来查找所需方法和属性。

优点:可以在sup的原型对象中添加的方法可以实现共享,利于代码的复用

缺点:如果sub中的引用类型的属性会被所有实例化的对象共享,不能保证各实例化对象的不同状态。

 

为了解决原型链继承这个缺点,我们引用了借用构造函数继承的方法:

function sup(){

    this.num=[1,2,3];

}

function sub(){

    sup.call(this);

}

var instance1=new sub();

instance1.num.push('4');

var instance2=new sub();

instanc2.num.push('5');

借用构造函数继承:

原理:

就是通过这行关键代码来实现继“sup.call(this);”,这行代码的意思就是,在sub的词法环境中我们在全局作用域中调用sup函数(借用sup的函数),所以执行完这行代码一行以后,sub就继承了sup。之后在实例化sub时,每个实例化对象,都会有num属性的副本,这其中就是通过this的指向来完成的。如:实例化instance1时,sub中的this就会指向instance1,而sub会借用sup的this,所以每个实例化的num属性都会不一样,这样就可以实现各实例化对象的不同状态。

优点:可以实现各实例化对象的不同状态;

缺点:不利于代码的复用

 

综合上面两种继承方法的优缺点,就引出了第三种继承方案:组合继承

function sup(name){

    this.name=name;

    this.num=[1,2,3]

}

sup.prototype.sayName=function(){

    alert(this.name);

}

function sub(){

    sup.call(this);

}

sub.prototype=new sup();

sub.prototype.construcor=sub;

var instance1=new sub();

instance1.num.push('4');

instance1.sayName();

var instance2=new sub();

instanc2.num.push('5');

instance2.sayName();

组合继承:

原理:综合以上两种方式的原理来实现;

优点:可以让sub的不同实例化对象分别有自己的状态属性,而且又可以使用相同的方法

缺点:两次调用sup构造函数,不利于性能优化

 

为了解决组合继承这个缺点,我们引用了寄生式组合继承的方法:

function imp(sub,sup){

    var proto=Object(sup.prototype);

    proto.constructor=sup;

    sub.prototype=proto;

}

function sup(name){

    this.name=name;

    this.num=[1,2,3];

}

sup.prototype.sayName=function(){

    alert(this.name);

}

function sub(){

    sup.call(this);

}

imp(sub,sup);

寄生式组合继承:

原理,之前说的两次调用sup构造函数,第一次是sup.call(this);第二次是创建sub对象原型的时候;分析第二次调用sup构造函数,其实实质上就是我们需要的只是sup原型对象的一个副本,说白了就是希望sub原型对象的_proto_属性指向sup的原型对象。为了解决这个问题,我们可以用一个函数封装来实现

function imp(sub,sup){

    var proto=Object(sup.prototype);

    proto.constructor=sup;

    sub.prototype=proto;

}

只要我们创建一个sup的原型对象,在把sup原型对象的construct指或sup,最后在把sub的原型对象指向sup的原型对象。我们就可以实现继承sup原型对象上的方法了

优点:避免在sub的原型对象上创建不必要的属性。

 

 

说完了我对高程上继承的理解,我说说老师课上继承的两种方式:1、类继承;2、原型继承

其一:类继承

(function(){

    function classA(){};

    classA.classMethod=function(){};

    classA.prototype.api=function(){};

    

    function classB(){

        classA.apply(this,arguments);

    }

    classB.prototype=new classA();

    classB.prototype.constructor=classB;

    classB.prototype.api=function(){

        classA.prototype.api.call(this,arguments);

    }

    var b=new classB()

})()

关于这段代码,我提出两个疑问:

疑惑一:

classB.prototype.constructor=classB;

关于这句语句:

我的理解:将classB原型对象上的constructor指回classB;

原因是执行完classB.prototype=new classA();以后classB的原型对象指向了classA的原型对象,而classA的原型对象中的constructor属性是指向classA的

我的疑惑:如果不加这句语句,那么classB原型对象上的constructor属性指向哪里?是classA吗?(我理解的constructor属性指向prototype属性所在的构造函数)如果不加这句语句,那么通过classB实例化的对象b,b的构造器会指向classA不会指向classB吗?对原形链有什么影响吗?请老师好好帮我分析分析,我实在想不出!

疑惑二:

classA.prototype.api.call(this,arguments);

关于这句语句:

我的理解:就是在classB的原型对象上添加api方法。我认为在classA的原型对象中已经添加了api方法,我们可以通过原型继承(原型链),在classB实例化的对象中访问到api方法,不需要再在classA的原型对象上在调用一次这个方法.请问老师这样写还有其他目的吗?还是我的理解不准确?

 

其二:原型继承

(function(){

    var proto={

        action1:function(){}

    }

    var obj=Object.create(proto);

})()

对于原型继承我的理解是:它和之前说的原型链继承有同样的弊端,不利于每个实例对象的属性状态的不同的保存。

这种继承还存在兼容性,我们可以封装一个函数解决。

 

以上就是我对继承的理解,因为还是学生,没有实践经历,理解层次还处于理论阶段,可以在以后实践会有不同吧!慢慢来。

在前端自学的路上,遇到好多问题,想对继承方法的命名不同,导致我不同的理解,弄混之前的理解。后现在觉得名字什么的只是一个说法,理解才是最重要的。所以我们可以在应用中针对不同的案例实现不同的继承方法

posted @ 2016-05-26 13:13  sakura丶shadow  阅读(202)  评论(0编辑  收藏  举报