文章目录
面向对象
几种模式
工厂模式
- 接收参数,内部构造一个对象并return
function Function1(args){
var obj = new Object();
obj.args = args;
//添加方法
obj.littleFuction = function() {
}
return obj;
}
构造函数模式
特点:
- 不显式的创建对象,直接将方法、属性赋给this对象
- 不return
- 需要实例化(new)
- 每个成员无法得到复用
Tips: 任何函数如果通过new操作符来调用,就可以作为构造函数;如果不用new操作符,则当普通函数(this则指向Global对象,浏览器中即window对象)
function Function2(args) {
this.args = args;
//添加方法
this.littleFuction = function() {
}
}
//使用
var test = new Function2(args);
原型模式
每个对象都有一个prototype属性,该属性是一个指针,指向一个包含所有实例共享的属性和方法的对象(原型对象)。
constructor属性:在自定义构造函数后,原型对象会默认取得改属性,constructor是指向prototype属性所在函数的指针。
Tips:
- 对象实例添加与原型对象同名属性时,不能重写,只会覆盖;delete操作符可删除实例属性。
- 原型具有动态性,实例的创建和原型之间是松散的,即先创建实例后修改原型也ok。随时可为原型添加属性和方法。
- 特别注意:如果重写原型对象(用对象字面量重写——object.prototype = { }),会切断现有原型与任何之前已经存在实例之间的联系。即原先new的实例无法引用的还是最初的原型。
原型对象存在的问题:
- 所有实例共享属性和方法,对于引用类型值就不可独立开来,容易出现问题
function Function3() {
}
//给原型添加属性
Function3.prototype.args = new_args;
//给原型添加方法
Function3.prototype.littleFuction = function() {
alert(this.args);//this指向的就是原型
}
//创建实例
var instance = new Function3();
instance.args = another_args;//只是这个实例的属性会覆盖掉原型的属性
instance.prototype = {
agrs: another_args
}//这样则会重写原型属性
组合使用构造函数模式和原型模式
- 构造函数模式用于定义实例属性
- 原型模式用于定义方法和共享的属性
//构造函数模式
function Function4(args) {
//定义实例属性
this.args = args;
}
//原型模式
Function4.prototype = {
share_args: share_args;
littleFuction : function() {
alert(this.args);
}
}
动态原型模式
通过构造函数来初始化原型。
通过 if 语句判断在调用构造方法时,参数是否有传某个方法,来决定是否需要初始化原型(就是采用原型模式来添加方法或属性)。
寄生构造函数模式
工厂模式 + new 操作符实例对象,调用构造函数(其实就是工厂模式的函数)
返回的对象与构造函数或者构造函数的原型属性之间没有关系
稳妥构造函数模式
与寄生构造函数模式类似,不同之处在于:
- 新创建的对象的实例方法不引用this
- 不使用new调用构造函数
其实就是函数传进来的值无法直接访问,因为没有了this和new,没有引用指向这些值。
想访问值可以添加方法,类似java的getter和setter
优点:数据比较安全
继承
ES支持实现继承,不支持接口继承。而实现继承主要靠原型链实现
原型链
何为原型链?
-
其实就是让原型对象包含的指向构造函数的内部指针,改为指向另一个原型对象,从而层层递进,形成原型链
-
代码上实现就是将 子类原型的prototype指针 指向 超类原型的实例。
//超类原型 function SuperType() { } //子类原型 function SubType() { } //继承 SuperType SubType.prototype = new SuperType();
事实上,所有的函数默认原型都是Object的实例,所以默认原型都会包含一个内部指针,指向Object.prototype。
我们也可以知道访问实例属性的本质:一步一步往上直至原型链末端搜索查找
Tips
- 给原型添加方法一定要放在替换原型的语句之后。即超原型的实例赋给子原型后,再添加(重写)子原型或超原型方法
- 通过原型链实现继承时,不能使用对象字面量创建原型方法。
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数
借用构造函数
在子类型构造函数内部调用超类型
代码上实现:超类型函数调用 call(this, args) 或 apply(this, args) 方法。可传参数,意味着相对原型链有个很大的优势,可以在子类构造函数中向超类构造函数传递参数
缺点:方法都在构造函数中定义,无法函数复用。
//超类原型
function SuperType() {
}
//子类原型
function SubType() {
//继承 SuperType
SuperType.call(this);
}
组合继承
实质就是把原型链和借用构造函数组合使用
Tips:
- 无论什么情况下都会调用两次超类型函数
- 创建子类型原型时
- 子类型构造函数内部调用 call() 或 apply() 方法
//超类原型
function SuperType(args) {
this.args = args;
}
//子类原型
function SubType(args, another_args) {
//继承 SuperType 的属性
SuperType.call(this, args);
//也能拥有自己的属性
this.another_args = another_args;
}
//继承 SuperType
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
原型式继承
可以在不必预先定义构造函数的情况下实现继承,本质是执行给定对象的浅复制。复制得到的副本可以进一步改造。
必须要有一个对象作为另一个对象的基础。
var object = {
agrs: "args";
}
var another_object = Object.create(object);//create方法是es5新增的
var another_object = Object(object);//或者这样
可以做到让一个对象与另一个对象保持类似
寄生式继承
基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。
function creatObject(original) {
var clone = Object(original);
//可接着添加属性或方法,属于自己的,与源对象独立的
return clone;
}
var object = {
agrs: "args";
}
var another_object = creatObject.create(object);
与寄生构造函数和工厂模式类似
寄生组合式继承
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);//创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
寄生组合式继承是实现基于类型继承的最有效方式
函数与闭包
js 定义函数的形式有两种
- 函数声明
- 函数表达式
了解函数声明提升
函数声明提升:在执行代码前会先读取函数声明。即代码顺序可以先调用再声明。但只对函数声明有效,对函数表达式则不行。
对于函数表达式(匿名函数),使用前必须先赋值。
变量作用域
- 函数内部可以访问外部变量,而函数外部无法读取函数的局部变量
- 没有用var声明的变量,默认为全局变量
每个函数被调用时,会创建他的执行环境,且执行环境有相应的作用域链,作用域链本质上是一个指向变量对象的指针列表,只引用不包含变量对象。而变量对象又有本地活动对象和全局变量对象之分。
匿名函数的执行环境具有全局性
何为闭包?
闭包 :指有权访问另一个函数作用域中的变量的函数
可以把闭包简单理解成"定义在一个函数内部的函数"。
function f1() {
var args = 'hello';
return function() {
alert(args);
}
}
函数 f1 返回的匿名函数就是闭包。
需要注意的是,匿名函数从 f1() 被返回后,它的作用域是包含f1的局部活动对象和全局活动对象的。而且,f1()函数执行完后,其执行环境的作用域链会被销毁,但它的活动对象依然留在内存中,只有直到匿名函数被销毁它才会销毁。
var res = f1();//创建函数
var res2 = res();//调用函数(匿名函数)
res = null;//解除对匿名函数的引用,以便垃圾回收机制回收,释放内存
Tips:
-
前面说过,闭包会引用包含函数的整个活动对象,也就是说闭包会使得函数中的变量都被保存在内存中,这样内存消耗很大,因此不能滥用闭包。
-
匿名函数的this对象通常指向window(执行环境具有全局性),但要视具体情况。下面是书上的例子:
var name = "The Window"; //第一个例子 var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());//"The Window" //第二个例子 var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());//"My Object"
- 第一个例子:调用 getNameFuncth 后,返回匿名函数。因为每个函数被调用时,会自动取得 this 和 argument 两个特殊变量。而内部函数搜索这两个变量时,只会搜索到其活动对象为止。因此访问不到外部函数中的 this 和 argument。
- 第二个例子:因为把 this 赋值给 that ,函数返回后,that 仍然引用着 object ,所以闭包可以访问。
闭包的用途
模仿块级作用域
js本身没有块级作用域的概念,可以用匿名函数模仿
- 创建并立即调用一个函数
- 执行完,不会在内存中留下对该函数的引用,函数内部的所有变量都会被立即销毁(除了那些被赋值给外部作用域的变量)。
//第一种
var blockFunction = function() {
//块级作用域
}
blockFunction();//立即调用
//第二种 函数表达式
(function() {
//块级作用域
})();
在对象中创建私有变量
相关概念:
- 特权方法:有权访问私有变量和私有函数的公有方法。
在函数内部创建闭包,可以利用闭包创建特权方法。
有如下两种方法:
- 第一种,在构造函数中定义特权方法
function MyFunction() {
//私有变量
var privateVariable = 1;
//私有函数
function privateFunction() {
return "I'm a private function"
}
//特权方法 其实就是个闭包
this.publicMethod = function() {
//访问私有变量
privateVariable++;
//返回私有函数
return privateFunction;
}
}
优点:可以做到私有变量只能通过特权方法才能访问和修改。
缺点:因为采用构造函数模式,对每个实例都会创建同样一组新方法。
- 第二种,使用原型模式,在私有作用域中定义私有变量或函数。
(function() {
//私有变量
var privateVariable = 1;
//私有函数
function privateFunction() {
return "I'm a private function"
}
//构造函数 没有用var声明,会变成全局变量,但严格模式下会报错
MyFunction() {
};
//特权方法
MyFunction。prototype.publicMethod = function() {
//访问私有变量
privateVariable++;
//返回私有函数
return privateFunction;
}
})();
优点:私有变量和私有函数所有实例共享。
缺点:每个实例没有自己的私有变量。
使用模块模式为单例创建私有变量和特权方法
var singleton = function() {
//私有变量
var privateVariable = 1;
//私有函数
function privateFunction() {
return "I'm a private function"
}
//返回对象的匿名函数
return {
//公有属性
publicProperty: "I'm a public property"
//特权方法
publicMethod : function() {
//访问私有变量
privateVariable++;
//返回私有函数
return privateFunction;
}
}
}();
使用场景:必须创建一个对象并以某些数据对其进行初始化,同时还需要公开一些能够访问私有数据的方法。
增强的模块模式
var singleton = function() {
//私有变量
var privateVariable = 1;
//私有函数
function privateFunction() {
return "I'm a private function"
}
//创建对象,是某种类型(xxx)的实例
var object = new xxx();
//公有属性
object.publicProperty = "I'm a public property"
//特权方法
object.publicMethod = function() {
//访问私有变量
privateVariable++;
//返回私有函数
return privateFunction;
};
//返回该对象
return object;
}();
适用于单例必须是给某种类型的实例