第6章 面向对象

面向对象的理解

面向对象:就是成立公司,类:经理这个类的属性,做事方法;会计这个类的属性,做事方法;等等这些规章制度就是类。经理new一个,马云,就是实例化一个。
好处:不管什么业务来了,每个岗位按照类的定义,可以运转解决问题。
坏处:每个岗位的工作流程要写清楚,至于这一次业务用得上还是用不上,甚至很多岗位这一次参与不参与都要写清楚,都要发工资养着。

Java语言使用定义class类来实现,
JavaScript语言使用原型及原型链来实现。

面向过程的理解

面向过程:一个业务来了,根据需要编写一套工作流程让电脑处理。
好处:针对一项业务,没有多余的代码要写,没有涉及到的事情可以不写出来。
坏处:每项业务来了都要重写一套代码

JavaScript原型及原型链继承

原型
JavaScript中的对象是基于原型的,原型是其它对象的基础,定义并实现一个新对象必须包含成员的列表。

当我们创建一个函数时,它自动获得一个prototype属性,这个属性同时也是一个对象,它包含了所有实例共享的属性和方法。使用原型的好处是:可以让所有对象实例共享它所包含的属性和方法,如下所示:

Object 对象具有下列属性:
constructor
对创建对象的函数的引用(指针)。对于Object对象,该指针指向原始的Object()函数。

Prototype
对该对象的对象原型的引用。对于所有的对象,它默认返回Object对象的一个实例。

**Object 对象还具有几个方法:**hasOwnProperty(property)
判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”))

IsPrototypeOf(object)
判断该对象是否为另一个对象的原型。

PropertyIsEnumerable
判断给定的属性是否可以用for…in语句进行枚举。

ToString()
返回对象的原始字符串表示。对于Object对象,ECMA-262没有定义这个值,所以不同的ECMAScript实现具有不同的值。

ValueOf()
返回最适合该对象的原始值。对于许多对象,该方法返回的值都与ToString()的返回值相同。

function  Person(name,age){
	this.name=name;
	this.age=age;
}
Person.prototype.getName=function(){
	return this.name;
}
Person.prototype.getAge=function(){
	return this.age;
}

var Person1=new Person('sam',24);
Person1.getName();

var Person2=new Person('dary',30);
Person2.getAge();

原型共享方法的原理

无论什么时候,只要创建了一个新函数,就会自动为函数创建一个prototype属性,在默认情况下,所有prototype属性都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。
当调用构造函数创建的实例后,该实例的内部包含一个指针(proto),指向原型对象。这个内部属性_proto_在Firefox,chrome,safari浏览器中对开发者来说是可见的。如下图展示了各个对象的关系:
在这里插入图片描述
Person的两个实例Person1和Person2的内部属性_proto_指向了原型对象,而Person.prototype.constructor又指回了Person,也就是实例与构造函数之间没有直接的关系。

原型链继承

原型链作为实现继承的主要方法,其基本思想是:让原型对象等于另一个类型的实例,这样原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例,如此层层递进,就构成了实例与原型的链条,这个链条就称之为原型链

我们可以在Person1的实例中调用isPrototypeOf()方法,但是我们并没有在Person的原型对象中定义该方法,我们知道,所有的引用类型都默认继承Object,而这个继承也正是通过原型链实现的,其实现过程如下所示:
在这里插入图片描述

function SuperClass(name){
    this.name=name;
}
SuperClass.prototype.getName=function(){
        return this.name;
}
function SubClass(name,age){
        //继承属性
        SuperClass.call(this.name);
    }
}
//继承方法
SubClass.prototype=new SuperClass();
SubClass.prototype.getAge=function(){
        return this.age;
}
var instance=new SubClass('sam',25);
console.log(instance.getName());

在这里插入图片描述

function SuperClass(name){
    this.name=name;
}
SuperClass.prototype.getName=function(){
        return this.name;
}
function SubClass(name,age){
        //继承属性
        SuperClass.call(this.name);
    }
}
//继承方法
SubClass.prototype=new SuperClass();
SubClass.prototype={
        getAge:function(){
            return this.age;
        }
}
var instance=new SubClass('sam',25);
console.log(instance.getName());
//Object #<Object>has no method'getName'

以上代码先SuperClass.prototype的实例赋值给原型,然后又将原型替换成一个对象字面量,这里就相当于重写了原型对象,切断了原有的原型链,现在的原型对象只是Object的实例,SubClass和SuperClass已经没有关系了。

解析对象成员及性能问题

解析对象成员的过程同解析变量的过程很类似,都是经历一次搜索的过程,当调用instance.getName()方法时,首先会在实例中查找有没有这个方法,如果实例中存在这个方法则直接调用,如没有找到,则到SubClass.prototype的原型上去找,如找到则停止搜索,如没有找到,则继续向SuperClass.prototype的原型上去找,直到getName()找到并执行为止。当我们调用的hasOwnProerty()方法时,都是通过这种搜索方式在Object.prototype上找到的并执行。
当调用instance.toString()方法时,搜索过程必须深入原型链中直到找到对象成员“toString”,对象在原型链中存在的位置越深,找到它就越慢。每次深入原型链都会有性能上的损失,搜索实例队员比直接量或局部变量中访问数据的代价要高。

作用域链和原型链的比较

讲完了作用域链和原型链,我们可以比较一下。作用域链的作用主要用于查找标识符,当作用域需要查询变量的时候会沿着作用域链依次查找,如果找到标识符就会停止搜索,否则将会沿着作用域链依次向后查找,直到作用域链的结尾。而原型链是用于查找引用类型的属性,查找属性会沿着原型链依次进行,如果找到该属性会停止搜索并做相应的操作,否则将会沿着原型链依次查找直到结尾。

在这里插入图片描述

posted @ 2022-04-06 23:53  szmtjs10  阅读(22)  评论(0)    收藏  举报