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

来源:互联网 发布:win10开始键没反应知乎 编辑:程序博客网 时间:2024/06/08 10:47

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