JS高级程序设计笔记(五):引用类型

Function

函数名就是函数体的引用。可以将函数名作为参数传递给另一个函数,也可以在一个函数内部返回另一个函数,以便在函数体外调用。例如Array.prototype.sort接收一个函数作为参数,该参数规定了比较的规则,如果我们想对数组中的对象按照某个属性来进行排序,可以这样做:

function compareByProperty(property) {
    return function(o1, o2) {
        var v1 = o1[property];
        var v2 = o2[property];

        if (v1 < v2) {
            return -1;
        } else if (v1 > v2) {
            return 1;
        } else {
            return 0;
        }
    }
}

var data = [{name: 'lily', age: 10}, {name: 'anna', age: 20}];
data.sort(compareByProperty('name'));
console.log(data);

1.this

this指向函数据以执行的环境对象。即上下文。是基于函数的执行环境绑定的。

通俗来说,就是调用这个函数的对象。
当函数在被调用的时候(进入执行的上下文时),会生成一个执行环境(excution context),内部有一个属性[scope]指向作用域链,作用域链中的每一项指向一个变量对象(variable object),变量对象中维护了当前函数自身的变量,并且此时活动对象会自动取得this和arguments的值。搜索这两个值只会搜索自身的变量对象,不会沿着作用域链进行搜索。

必须是在一个函数内部定义了一个函数,内部函数才能访问外部函数的变量对象,如果函数是在外部定义但被另一个函数调用了,是不能访问外部函数的变量对象的。

也就是说作用域链的生成和函数定义的位置有关,而this与arguments的值是与函数被调用的环境有关。

‘以下部分参考《JS权威指南》’:

函数的调用形式有:
- 函数
- 方法
- 构造函数
- 通过call和apply间接调用

通常作为函数来调用的时候不会使用this,但可以通过this来判断是否是严格模式。

this是关键字,不是变量,不能赋值。嵌套的函数不会从外部函数中继承this值,如果嵌套函数作为函数调用,this值为window或undefined(严格模式)。

function outter() {
    console.log('I am outter function!');
    function inner() {
        console.log('I am inner function');
        console.log( this === window ); //非严格模式true
    }      
    inner();
}

因此不要觉得嵌套函数的this指向调用外层函数的上下文或者是外层函数,要取得外层函数的this,需要将this保存到一个变量中。

2.arguments.callee指向当前执行的函数;
arguments.callee.caller指向调用当前函数的函数,如果当前函数处于全局作用域中,则caller为null.

3.call和apply
call和apply可以扩充作用域,使得对象和方法不需要有耦合关系。

4.闭包

  • 作用域链和变量对象

    函数在进入执行环境(开始调用还未执行到具体语句)时候,会产生一个excution context其中有一个属性[scope]指向了作用域链,而这个作用域链中的每一项又指向了可访问到的变量对象。
    变量对象保存了当前执行环境中可访问到的变量,当函数被调用时有arguments、形参、通过function声明的局部函数是具有具体的值的,而其他通过var声明的变量一开始是undefined,直到执行到当前的语句才被赋予具体的值。

  • 闭包

    闭包就是可以访问到外部函数中变量的函数,即在闭包的作用域链中持有对外部函数变量对象的引用。当外部函数执行完毕后,其作用域链被销毁,如果返回的闭包还存在,则仍然持有对外部函数的变量对象的引用。因此使用闭包是很消耗内存的。

    此外,闭包只能取得包含函数中任何变量的最后一个值。在闭包中引用某个变量时,会沿着作用域链在变量对象中进行搜索。
    例如:

function createFun() {
    var result = [];
    for (var i = 0; i < 10; i++) {
        result[i] = function() {
            console.log(i);
        };
    }
    return result;
}
var res = createFun();
res[0]();  //10

result数组中存了10个函数并返回了,可以在外部使用。当这些内部函数调用的时候,执行到console.log(i)时会从变量对象中搜寻该标识符,在自己的变量对象中没有找到,就会沿着作用域链向上查找外部函数的变量对象,现在,在外部函数的变量对象中是这样的内容:

VO{
    result: [//10个匿名函数的引用],
    i: 10
}

此时i已经为10了。
可以在外部再包裹一层,来保存每一次的i值:

function createFun() {
    var result = [];
    for (var i = 0; i < 10; i++) {
        result[i] = function(num) {
            return function() {
                console.log(num);
            };
        }(i);
    }
    return result;
}
var res = createFun();
res[1](); //1

当调用某个闭包函数resindex时,需要访问num变量,此时会沿着作用域链进行搜索,闭包的作用域链是这样的:

VO res[index]: {
    //nothing
}
VO 包裹res[index]的匿名函数: {
    arguments: num,
    num: i //是指当前的i,每次循环当前的i都被保存下来
}
VO createFun: {
    result: [//10个匿名函数的引用],
    i: 10
}

因此当res[index]沿着作用域链搜索时,会在包裹它的匿名函数的变量对象中找到num,而这个num的值是对应的i的值。

闭包中的this:
每个函数被调用时,其活动对象自动获得this和arguments,在搜索时只搜索自己的活动对象,所以闭包不能获得外部函数的这两个值。而闭包通常是在全局环境中调用,因此闭包内部的this通常是指window。

5.块级作用域

JS中对于重复声明的变量会视而不见。
立即执行的匿名函数可以模拟块级作用域,这样可以避免向全局环境中增加过多的变量。

6.私有变量

在函数内部定义局部变量和局部函数,然后返回一个闭包,在这个闭包中对局部变量和局部函数进行访问。闭包是外部访问局部变量/函数的唯一入口,该闭包称作特权函数。特权函数有两种形式:

  • 作为构造函数的方法:
function Func() {
    var privateVar = ...;
    function privateFunc() {}

    this.publicMethod = function() {
        privateVar++;
        return privateFunc();
    };
}

该方式的缺点是,每个实例都要创建一个自己的方法。

  • 静态私有变量
function() {
    var privateVar = ...;
    function privateFunc() {}

    MyObject = function() {}; //构造函数
    MyObject.prototype.publicMethod = function() {//原型增强复用
        privateVar++;
        return privateFunc();
    };
})();

该方式需要一个全局的构造函数(此处只能用函数表达式),并在原型中定义特权方法,该特权方法是一个闭包,能够取得外部函数的私有变量和函数。该方式MyObject的所有实例共享私有变量和私有函数
注意此处的MyObject虽然是一个全局变量,但该构造函数的声明仍然是在匿名函数内部进行的,因此,在该构造函数内部也是可以访问到外部匿名函数的私有变量的。注意闭包的定义是:

在一个函数内部定义了另一个函数,就创建了闭包

  • 模块模式
    指为单例增加私有变量和特权方法:
var singleton = function() {
    var privateVar = ...;
    function privateFunc() {}

    return {
        publicProperty: ...,
        publicMethod: function() {
            privateVar++;
            return privateFunc();
        }
    };
}();

这里的单例都是Object的实例。

  • 增强的模块模式
    适用于单例必须是某个类的实例:
var singleton = function() {
    var privateVar = ...;
    function privateFunc() {}

    var obj = new MyObject(); //特定的类型实例
    obj.publicProperty =  ...;
    obj.publicMethod = function() {
            privateVar++;
            return privateFunc();
    };
    return obj;
}();

Array

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值