匿名函数

工厂模式

假如我们把猫看成是一类,都有名字和颜色的属性,那么,每一只猫就是这个类的实例,我们可以使用字面量方式定义对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var cat1 = {
name:"大黄",
color:"黄色",
say:function(){
alert("miao")
}
};

var cat2 = {
name:"小黑",
color:"黑色",
say:function(){
alert("miao")
}
};
  • 这样创建对象,一是创建对象比较麻烦,二是实例与原型(猫类)之间,没有任何联系。
  • 为了简化代码书写,我们可以写一个函数批量创建对象,也就是使用“工厂模式”
    (工厂模式中的函数,首字母大写):
1
2
3
4
5
6
7
8
9
function Cat(n,c){
return {
name:n,
color:c,
say:function(){
alert("miao")
}
}
}

生成实例对象,就等于是在调用函数

1
2
var cat1 = Cat("大黄","黄色");
var cat2 = Cat("小黑","黑色");

这样代码虽然简单了,但是实例之间还是没有什么联系。

构造函数

  • 为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
  • 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
  • 构造函数首字母大写,没有return语句,没有显式地创建对象。
  • 构造函数中的this,指向的是实例化的对象
    例如:
1
2
3
4
function Cat(n,c){
this.name=n;
this.color=c;
}
  • 生成实例对象
1
2
var cat1 = new Cat("大黄","黄色")
var cat2 = new Cat("小黑","黑色")
  • 这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。
1
2
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
  • Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。
  • instanceof不认为基本类型值的变量是对象。
1
2
3
4
5
6
7
8
9
10
11
var txt='adsdf';
alert(txt instanceof String);//false

var age=123123;
alert(age instanceof Number);//false

var re=/\d/;
alert(re instanceof RegExp);//true

var arr=[];
alert(arr instanceof Array)//true

原型模式和原型链

  • 每类对象的实例对象,都有可能会有一些相同的属性,或者相同的功能,如果都写在构造函数上,会造成重复的内容,占用更多的内存。
  • 为了解决这种代码的重复
  • 构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
  • 我们可以把那些不变的属性和方法,直接定义在prototype对象上。让实例对象共享其属性和方法。
1
2
3
4
5
Cat.protoType.say = function(){
alert("miao")
}
cat1.say()//miao
cat2.say()//miao
  • 所有的函数都是 Function 的实例。在构造函数上都有一个原型属性—prototype,prototype也是一个对象;这个对象上有一个 constructor 属性,该属性指向的就是构造函数。
  • 而实例对象上有一个_proto_属性,该属性也指向原型对象,该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用。

组合使用构造函数模式和原型模式

  1. 构造函数与原型模式组合的模式是目前使用最广泛、认同度最高的一种创建定义类型的方法。构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。

  2. 每个实例都会有自己的一份实例属性的副本,同时共享着对方法的引用,最大限度节省内存。同时,这种组合模式还支持向构造函数传递参数,集合了两种模式的优点。

动态原型模式

  • 动态原型模式就是把所有信息都封装在构造函数中,通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

寄生构造函数模式

  • 寄生构造函数模式的基本思想,是创建一个用于封装创建对象的函数。主要用于创建一个自定义类型的函数。

稳妥构造函数模式

  • 稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。适合在一些安全的环境中(禁用this和new的环境),或者防止数据被其他应用程序调用时使用。
  • 稳妥构造函数遵循与寄生构造函数类似的模式,只是有两点不同:
    1. 新创建对象的实例方法不引用this;
    2. 不使用new调用构造函数。

原型链

  • 原型链可以简单理解为原型组成的链,对象的_proto_就是其原型,而原型也是一个对象,也有_proto_属性。原型的_proto_又是原型的原型,这样可以一直向上找,直到找到Object的原型,就是这条原型链的顶端。

借用构造函数,也称伪造对象或经典继承

  • 借用构造函数实现继承的基本思想是:在子类的构造函数中调用超类的构造函数。该函数可以通过apply()方法和call()方法在新创建的对象中执行构造函数。

    • 优点:相对于原型链而言,借用构造函数可以在子类构造函数中向超类构造函数传递数据。
    • 缺点:构造函数的方法都在构造函数中定义,因此无法实现函数复用。另外,子类无法看见在超类中定义的方法,导致所有类型只能使用构造函数模式。

组合继承

  • 组合继承,又称为伪经典继承。指:将原型链和借用构造函数的技术组合在一起,从而发挥两者长处的一种继承模式。

    其思想是:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。这样,我们就可以实现通过在原型上定义方法进而实现函数复用,又能保证每个实例都有自己的属性,解决原型链中存在的引用类型属性共享问题。

原型式继承

  • 原型式继承,即:一个构造方法,通过原型链的方式继承另一个构造函数。

寄生式继承

  • 寄生式继承,即:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后返回该对象。

寄生组合式继承

  • 寄生组合式继承是JS最常用的继承模式,其最大的问题就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类原型时,另一次是在子类构造函数内部。

函数操作

函数表达式

使用函数表达式创建的函数,必须先赋值再调用。(即:只有函数声明式才有函数提升,函数表达式没有函数提升。)

1
2
3
4
5
fn2(); // 不能在函数之前调用。
var fn2 = function(){
console.log("2.演示函数表达式");
}
fn2(); // 只能在函数之前调用。

虽然函数表达式没有函数提升,但是它也有自己的一些优点。

递归函数

  • 递归函数,就是在函数体内调用本函数。最简单的例子就是计算阶乘: 123…。

  • 递归的缺点:如果递归函数的终止条件不明确甚至缺失,会导致该函数长时间运行,使用户界面处于假死状态。另外,当递归使用太多甚至超过最大调用栈内存容量时,浏览器会报错。

闭包

  • 闭包,是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数。
  • JS中,一般为了给某个函数声明一些只有该函数才能使用的局部变量时就会使用闭包,这样可以减少全局作用域中的变量,净化全局作用域。但是闭包会占用更多的内存。

模仿块级作用域

  • JS中没有块级作用域,我们可以进行模仿块级作用域。

使用方式:
简单来讲,就是定义并立即调用一个匿名函数。
具体步骤,就是将匿名函数声明(函数表达式)在一对圆括号中,而紧随其后的一对圆括号会立即调用该函数。

私有变量

  • 严格来讲,JS中没有私有成员的概念,所有对象属性都是公开的。
  • 但是,任何在函数中定义的变量都可以认为是私有变量,因为不能在函数外部访问。
  • 私有变量包括函数参数,局部变量以及在函数内部定义的其他函数。
  • 闭包函数可以持有这些私有变量,并在外部访问。