码迷,mamicode.com
首页 > 编程语言 > 详细

JavaScript深度学习

时间:2015-08-05 17:52:33      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:

1、Javascript作用域原理

var name = ‘laruence‘;
    function echo() {
         alert(name);   //laruence
         var name = ‘eve‘;
         alert(name);   //eve
         alert(age);    //脚本错误
    }
     
    echo();

运行结果是什么呢?

上面的问题, 我相信会有很多人会认为应该像注释那样的。

执行了一次之后发现第一次alert出来确是undefined。为什么呢?

JS权威指南中有一句很精辟的描述: ”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”

其实在JS中:”一切皆是对象, 函数也是”。

在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.
在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.

看个例子:

  1.      var func = function(lps, rps){
  2.           var name = ‘laruence‘;
  3.           ........
  4.      }
  5.      func();

在调用func的时候, 会创建一个活动对象(假设为aObj, 由JS引擎预编译时刻创建, 后面会介绍),并创建arguments属性, 然后会给这个对象添加俩个命名属性aObj.lps, aObj.rps;

对于每一个在这个函数中申明的局部变量和函数定义, 都作为该活动对象的同名命名属性.

有了上面的作用域链, 在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义。

注意到, 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候, 所以如下面的例子:

  1. function factory() {
  2.      var name = ‘laruence‘;
  3.      var intro = function(){
  4.           alert(‘I am ‘ + name);
  5.      }
  6.      return intro;
  7. }
  8.  
  9. function app(para){
  10.      var name = para;
  11.      var func = factory();
  12.      func();
  13. }
  14.  
  15. app(‘eve‘);

当调用app的时候, scope chain是由: {window活动对象(全局)}->{app的活动对象} 组成.

在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为’eve’的属性para;

此时的scope chain如下:

    1. [[scope chain]] = [
    2. {
    3.      para : ‘eve‘,
    4.      name : undefined,
    5.      func : undefined,
    6.      arguments : []
    7. }, {
    8.      window call object
    9. }

    注意到, 此时的作用域链中, 并不包含app的活动对象.

    在定义intro函数的时候, intro函数的[[scope]]为:

    1. [[scope chain]] = [
    2. {
    3.      name : ‘laruence‘,
    4.      intor : undefined
    5. }, {
    6.      window call object
    7. }
    8. ]

从factory函数返回以后,在app体内调用intor的时候, 发生了标识符解析, 而此时的sope chain是:

  1. [[scope chain]] = [
  2. {
  3.      intro call object
  4. }, {
  5.      name : ‘laruence‘,
  6.      intor : undefined
  7. }, {
  8.      window call object
  9. }
  10. ]

因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是’laruence’.

所以运行结果是:

  I am laruence

现在, 大家对”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”这句话, 应该有了个全面的认识了吧?

2、原型和原型链

 

原型使用方式1

var Calculator = function (decimalDigits, tax) {

  this.decimalDigits = decimalDigits;

  this.tax = tax;

};

然后,通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型。

Calculator.prototype = {

  add: function (x, y) {

    return x + y;

  },

  subtract: function (x, y) {

    return x - y;

   }

};

//alert((new Calculator()).add(1, 3));

这样,我们就可以new Calculator对象以后,就可以调用add方法来计算结果了。

原型使用方式2

第二种方式是,在赋值原型prototype的时候使用function立即执行的表达式来赋值,即如下格式:

Calculator.prototype = function () { } ();

它的好处在前面的帖子里已经知道了,就是可以封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果,修改后的代码如下

Calculator.prototype = function () {

  add = function (x, y) {

    return x + y;

  },

  subtract = function (x, y) {

    return x - y;

  }

  return {

    add: add,

    subtract: subtract

  }

} ();

//alert((new Calculator()).add(11, 3));

我们再来说一下如何分布来设置原型的每个属性吧。

var BaseCalculator = function () {

  //为每个实例都声明一个小数位数

  this.decimalDigits = 2;

};

//使用原型给BaseCalculator扩展2个对象方法

BaseCalculator.prototype.add = function (x, y) {

  return x + y;

};

BaseCalculator.prototype.subtract = function (x, y) {

  return x - y;

};

创建完上述代码以后,我们来开始:

var Calculator = function () {

  //为每个实例都声明一个数字

  this.tax = 5;

};

Calculator.prototype = new BaseCalculator();  

var calc = new Calculator();
alert(calc.add(1, 1));
//BaseCalculator 里声明的decimalDigits属性,在 Calculator里是可以访问到的
alert(calc.decimalDigits);

function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = ‘Hello World‘;

// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 创建Bar的一个新实例

// 原型链
test [Bar的实例]
    Bar.prototype [Foo的实例]
        { foo: ‘Hello World‘ }
        Foo.prototype
            {method: ...};
            Object.prototype
                {toString: ... /* etc. */};
3、闭包的样例

例子1:闭包中局部变量是引用而非拷贝

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
 
var sayAlert = say667();
sayAlert()

因此执行结果应该弹出的667而非666。

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

function setupSomeGlobals() {
    // Local variable that ends up within closure
    var num = 666;
    // Store some references to functions as global variables
    gAlertNumber = function() { alert(num); }
    gIncreaseNumber = function() { num++; }
    gSetNumber = function(x) { num = x; }
}
setupSomeGolbals(); // 为三个全局变量赋值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12

例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = ‘item‘ + list[i];
        result.push( function() {alert(item + ‘ ‘ + list[i])} );
    }
    return result;
}
 
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = ‘Hello Alice‘;
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。

例子5:每次函数调用的时候创建一个新的闭包

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        alert(‘num: ‘ + num +
        ‘\nanArray ‘ + anArray.toString() +
        ‘\nref.someVar ‘ + ref.someVar);
    }
}
closure1=newClosure(40,{someVar:‘closure 1‘});
closure2=newClosure(1000,{someVar:‘closure 2‘});
 
closure1(5); // num:45 anArray[1,2,3,45] ref:‘someVar closure1‘
closure2(-10);// num:990 anArray[1,2,3,990] ref:‘someVar closure2‘

JavaScript深度学习

标签:

原文地址:http://www.cnblogs.com/Copyrightcodetocompany/p/4705181.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!