JS 基础 —— ECMAScript 对象 笔记

来源:互联网 发布:数据库图片的类型 编辑:程序博客网 时间:2024/06/16 12:21

一、面对对象语言的四种基本能力

封装:把相关的信息(无论数据或方法)存储在对象中的能力

聚集:把一个对象存储在另一个对象内的能力

继承:由另一个类(或多个类)得来类的属性和方法的能力

多态:编写能以多种方法运行的函数或方法的能力

感觉理解又提高了。


二、对象使用

主要记录下对象废除、对象属性的早绑定与晚绑定。

1、对象废除:

ECMAScript 拥有无用存储单元收集程序(garbage collection routine),意味着不必专门销毁对象来释放内存。当再没有对对象的引用时,称该对象被废除(dereference)了。运行无用存储单元收集程序时,所有废除的对象都被销毁。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的局部变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。

把对象的所有引用都设置为 null,可以强制性地废除对象。

2、对象属性(包括普通属性与函数属性)的早绑定与晚绑定

所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。

早绑定(early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。在 Java 和 Visual Basic 这样的语言中,有了早绑定,就可以在开发环境中使用 IntelliSense(即给开发者提供对象中属性和方法列表的功能)。ECMAScript 不是强类型语言,所以不支持早绑定。

另一方面,晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性和方法即可。ECMAScript 中的所有变量都采用晚绑定方法。这样就允许执行大量的对象操作,而无任何惩罚。

理解:

早绑定就是在对象还未实例化前,其模型就可逐步确定的绑定方法。ECMAScript 不支持早绑定,所以编辑js代码时并不能提示该类已经具有的属性,因为其模型还未生成。


三、对象作用域

ECMAScript 中只有公共作用域(public),没有私有作用域(private)与受保护作用域(protected)。

对ECMAScript 只有公共作用域这点自己比较疑惑的是,对象内部定义的局部变量与局部方法其实相当于私有属性,但概念上还是要区别开:

对象属性:

通过this定义或prototype定义的属性,他们都处在公共作用域,类似于java与c++的public属性或方法,可被继承。该构造器的对象可直接访问。如:

function person(name,age){    this.name = name;    this.age = age;    this.greet = function(){        alert("Hi!");    }}

局部变量:

不看做对象属性,它们仅是本"类"的临时变量,但若实现闭包时,它不会被释放。该构造器的对象不可直接访问。


ECMAScript 中如何定义私有属性?

开发者共同约定:私有属性以下划线开始,以下划线结束,或只需以下划线开始。以此来辨别该属性是否是私有的,当然其本质还是公有的,只是开发中的约定。如:

obj._color_ = "blue";

obj._color

ECMAScript 静态作用域?

静态作用域中的属性或方法可通过类名直接引用,如java 中的静态变量与静态方法。ECMAScript 没有静态方法与属性,所有对象属性都只有该构造器的对象能访问:

function sayHello() {this.name = "name";}sayHello.propotype.getName=function(){alert("名字为:"+this.name)}alert(sayHello.name);//无法提示“name”alert(sayHello.getName());//无法提示“名字为name”

注意:构造器外定义的函数,只能用prototype 定义。

this 关键字的作用?

和java 与c++ 一样,构造器中的this 都指当前的对象,对函数属性中的this,指调用该函数属性的对象(意义相同)。

function obj(){var lVar = "局部变量";this.attrName = "属性变量";this.method = function(){//函数性质的属性变量var tempVar;tempVar = lVar + this.attrName;alert(tempVar);}}var obj1 = new obj();obj1.method();//提示:局部变量属性变量alert(obj1.attrName);//提示:属性变量alert(obj1.lVar);//提示:undefined,不能访问局部变量

理解:构造器内用var 定义的变量是临时变量,但可以被该构造器内部的方法使用(形成闭包),不能通过该构造器的对象直接访问。



四、定义"类"

方式一:

原始方式,最早时的使用方式,现很少用。

var obj = new Objec();obj.attrOne = "attrOne";obj.attrMethodOne = function(){alert("haha")}

方式二:

对方式一的改进,工厂方式。

function createObj(){    var obj = new Object();    obj.attrOne = "attrOne";    obj.attrMethodOne = function(alert("haha"));    return obj;}

方式三:

构造函数方式。即本身就是我们需要的类。

function obj(){    this.attrOne = "attrOne";    this.attrMethodOne = function(){alert("haha")}}

方式四:

原型方式。与构造方式相似,只是属性是通过继承原型获得。

function obj(){}obj.prototype.attrOne = "attrOne";obj.prototype.attrMethodOne = funtion(){alert("haha")}

原型方式缺点:继承的属性其值都是原型中的值(此处即"attrOne"与确定的函数属性的函数体),如果各对象之间该属性有差异,初始化后需再修改。

方式五:

方式三与方式四的混合方式,即构造函数方式与原型方式搭配使用。

优点:对于值相同的属性(相当于java 的静态变量),可用原型方式定义,对于对象间有差异的属性,采用构造函数方式。

//假设attrOne 属性在各对象间是有差异的,而函数性质的属性是相同的function obj(attrOne){    this.attrOne = attrOne;}obj.prototype.attrMethodOne = function(){alert("haha")}

方式六:

动态原型方式,对方式五的改进。方式五虽然灵活,但部分属性(包括普通属性与函数性质的属性)定义在了构造器外部,可能不太便于理解之中的关系。改进为:

function obj(attrOne){    this.attrOne = "attrOne";    if(typeof this._initialized == "undefined"){        obj.prototype.attrMethodOne = function(){alert("haha")}//注意不是this.prototype....        this._initialized = true;    }}

说明:实际是采用标记的方式,只在第一次执行时为构造器添加从原型继承过来的属性,从而将所有属性都集中写在obj 构造器中。

个人理解:方式六虽然所有属性都集中在了构造器中,但代码对齐方式上却有些问题,喜欢哪种方式看个人喜好吧

几点补充:

1、任意函数都可看做是Function "类"的对象,函数名就是该对象的引用;

2、基于第1条,函数属性的属性,其值可以在外部定义后,将引用传给该属性,如:

funtion attrMethodOne(){    alert("haha");}function obj(){    this.attrMehodOne = attrMethodOne;}

3、构造器可以传参数。


五、继承

继承方式:

和其他功能一样,ECMAScript 实现继承的方式不止一种。这是因为 JavaScript 中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。


方式一:

对象冒充。在构造器中使用被继承的"类"的引用,从而获得被继承"类"中的所有属性。

function ClassB(sColor) {    this.newMethod = ClassA;//ClassA被继承的"类"    this.newMethod(sColor);    delete this.newMethod;//去除对ClassA构造器的引用,对已获得的属性不影响}
注意:只有直接在构造器或构造器的函数性质属性中定义的对象属性才有用,如:

function obj(){this.attrName = "属性变量";function innerFun(){this.innerAttr = "此处的this不指obj 对象";console.log("this为:"+this.valueOf());}innerFun();this.innerFun2 = function(){this.innerAttr2 = "此处的this才指obj 对象";}this.innerFun2();}obj.prototype.method = function(){alert(this.innerAttr);//提示:undefinedalert(this.innerAttr2)//提示:此处的this 为obj 对象}var obj1 = new obj();//必须,只有构造器的对象能访问其属性与方法obj1.method();

另外,对象冒充可以实现多重继承(知道了只是将被继承的构造器作为内部函数性质的属性就不难理解了):

function ClassZ() {    this.newMethod = ClassX;    this.newMethod();    delete this.newMethod;    this.newMethod = ClassY;    this.newMethod();    delete this.newMethod;}

方式二:

call 方法或apply 方法。call 与apply 方法不仅能用于构造器内部的函数性质的方法,也能用于构造器(本质都是Function 对象)。

首先简单介绍call 与apply 方法,它们是将调用者(即Function 对象)用于参数指定的对象,区别在于提供Function 对象参数的方式不太一样,前者为直接的列表,后者为Array 数组形式。如:

aFuntionObj.call(someoneObj[,param1...]);aFuntionObj.apply(someoneObj[,aArrayObj]);

继承实例:

function ClassB(sColor, sName) {    //this.newMethod = ClassA;    //this.newMethod(color);    //delete this.newMethod;    ClassA.apply(this, new Array(sColor));    this.name = sName;    this.sayName = function () {        alert(this.name);    };}

方式三:

原型链。

回顾下通过原型定义属性的方式:

function obj(){}obj.prototype.normalAttr = "普通属性";obj.prototype.methodAttr = function(){...}

此处的prototype 即原型的引用,默认为Object 类,相当于默认继承Object ,所以修改prototype 可修改继承于哪个类。

function FClass(){this.attrOne = "父类普通属性";this.attrTwo = function(){alert("父类方法属性");}}function CClass(){this.attrOne = "子类普通属性";}//让CClass 继承于FClass类CClass.prototype = new FClass();//注意prototype 虽然是引用,但传的是父类的对象(引用),而不是父类的构造器名//实例化子类,看属性情况var subObj = new CClass();alert(subObj.attrOne);//提示:子类普通属性subObj.attrTwo();

说明:父类中同名的属性会被子类的同名属性覆盖掉(合理),无关位置先后。


方式四:

混合方式。上面三种方式的混合。

特别提点:第三种方式prototype 指向的是父类的一个实例,所以之后子类再通过prototype 增加或修改属性时不会对父类结构造成影响。验证:

function FClass(){this.attrOne = "父类普通属性";this.attrTwo = function(){alert("父类方法属性");}}function CClass(){this.attrOne = "子类普通属性";}//让CClass 继承于FClass类CClass.prototype = new FClass();//注意prototype 虽然是引用,但传的是父类的对象(引用),而不是父类的构造器名CClass.prototype.attrThree = "子类第三个属性";//实例化子类,看属性情况var subObj = new CClass();alert(subObj.attrOne);//提示:子类普通属性subObj.attrTwo();alert(subObj.attrThree);//提示:子类第三个属性//实例化父类,看是否被子类增加了attrThree 属性var supObj = new FClass();alert(supObj.attrThree);//提示:undefined

可见,子类继承父类后,再通过prototype 属性增加或修改属性不会对父类造成影响。




整理自:w3school





原创粉丝点击