js高级程序设计读书笔记

1. js介绍

js是为了实现网络交互而是设计的脚本语言,有以下三部分组成

  • ECMAScript,由ECMA-262定义,提供核心功能,其实web浏览器是其宿主环境之一,组成部分:语法,类型,语句,关键字,保留字,操作符,对象
  • 文档对象模型(DOM),提供访问和操作页面的接口和方法,DOM把整个页面映射为一个多层节点结构,根据其提供的API,开发人员可自如增删改换节点。
    DOM1=DOM Core(映射文档结构) + DOM html(对html的对象和方法)
    DOM2新模块:
  1. DOM视图(DOM views),跟踪不同文档的视图接口
    2 .DOM事件(DOM Events),定义事件和时间处理的接口
  2. DOM样式(DOM style),基于CSS为元素应用样式接口
  3. DOM遍历和范围(DOM Traversal and Range),遍历和操作文档树的接口
    DOM3新模块:
    .DOM验证(DOM Validation)文档验证方法并支持XML1.0规范
  • 浏览器对象模型(BOM)
    根本来说,处理浏览器的窗口和框架,但人们习惯将一下扩展也算成BOM的一部分
    1.弹出新浏览器窗口
    2.移动 关闭 缩放浏览器
  1. 提供浏览器详细信息的navigator对象
    4.浏览器所加载页面的详细信息location对象
    5.用户显示器分辨率信息screen对象
    6.对cookie的支持
    7.自定义对象

2.在html中使用js

通过<script>引入,常用属性
1.async,表示应该立即下载脚本,但不妨碍页面的其他操作,会在页面load之前进行
2.defer,表示文档可以延迟到文档全部被解析和显示后再执行
3.src,执行代码的外部文件
4.type,表示编写代码使用的脚本语言的内容类型(也成为MIME类型),一般是text/javascript(这个是默认的)
使用方式
1.直接放入内部元素,内部/script应替换成</script>,以免造成误解
2.嵌入式
一般放在body后面,这样在解析JS语言之前,页面内容已经完全展示在浏览器上,不要出现浏览器明显延迟的问题
<noscript>在不支持js的浏览器显示替代的内容

3.基本概念

  • 标识符
    驼峰大小写格式,如firstStudent
  • 注释
    //单行
    /*
    *多行
    */
  • 严格模式
    "use strict"
  • 变量
    松散类型
var message; //没初始化,因此是undefined

不使用var标识符的话就是全局变量

  • 类型typeof
    undefined,boolean, string,object,number,function
  • number:
    Infinity(正无穷)
    -Infinity(负无穷)
    isFinite()判断是否有穷
    NaN非数值isNaN()判断是否为数值
    将非数值转化为数值
    Number parseInt parseFloat
  • object对象
    初始化 var o = new object();
    属性方法
    Constructor创建当前对象的函数
    hasOwnProperty(propertyName)给定的属性在当前实例中是否出现
    ** isPrototypeOf(object) **别传入的对象是否为另一个对象的原型
    PropertyIsEnumerable判断给定的属性是否可以用 for...in 语句进行枚举。
    toLocaleString 方法返回执行环境对应的字符串。
    toString()对象字符串表示
    valueOf()返回对象的字符串数值或布尔表示
  • 语句
    if do-while while for switch
    for-in枚举
    lable-代码中添加标签,以便后来使用???
    break 跳出整个循环 continue跳出当前循环
    with 作用域设定到一个对象中

4.变量,作用域,内存问题

执行环境类型——全局,局部
其中内部环境可通过作用域链访问所有的外部环境,但外部环境不能内部环境的任何变量和函数

4.1延长作用域链

执行下列语句时,作用域延长

try-catch
with

  • 栗子1
function buildUrl(){  
     var qs="?debug=true";  
     with(location){  
         var url=href+qs;  
     }  
     return url;  
}  
var result=buildUrl();  
alert(result);  

输出: 静态页地址+qs的值。
原因:
1.由于with语句块中作用域的‘变量对象’是只读的,所以在他本层定义的标识符,不能存储到本层,而是存储到它的上一层作用域
2.with代码块中,javascript引擎对变量的处理方式是:先查找是不是该对象的属性,如果是,则停止。如果不是继续查找是不是局部变量。

var o={href:"sssss"};  
var href="1111";  
function buildUrl(){  
     var qs="?debug=true";       
     with(o){  
          href="2222";  
          var url=href+qs;  
     }      
     return url;  
}  
var result=buildUrl();  
alert(result);  
alert(href);  

输出: 结果:2222?debug=true + 1111

4.2 JavaScript作用域之没有块级作用域

<script>  
function test(o){  
    var i=0;  
    if(typeof o == "object"){  
        var j=0;  
        for(var k=0; k<10; k++){  
            document.writeln("k="+k);  
        }  
        document.writeln("k2="+k);  
    }     
    document.writeln("j="+j);     
}  
  
test(new String("pppp"));  
</script>  

输出结果为:k=0 k=1 k=2 k=3 k=4 k=5 k=6 k=7 k=8 k=9 +++k=10 j=0
这是因为,由于JavaScript中不存在块级作用域,因此函数中声明的所有变量,无论是在哪里声明的,在整个函数中它们都有定义。

<script>  
var scope="global";  
function f(){  
    alert(scope);  
    var scope="local";  
    alert(scope);     
}  
  
f();  
</script> 

结果:第一个alert输出:underfined而不是global,第二个alert输出local
与下列函数等价

function f(){  
    var scope;  
    alert(scope);  
    var scope="local";  
    alert(scope);         
}  

5.引用类型

5.1Object类型

创建有两种方式:
1.new操作符后跟Object构造函数

var person = new Object();
person.name = "Nicholas";
person.age = 29;

2.字面量表示法

var person = {
    name: "Nicholas",
    age: 20,
    5:true
};

这里所有的数值属性名都会被转换为字符串
访问方法

alert(person.name);
alert(person[name])

5.2Arrary类型

构造
var colors = ["red","green"];
var arrays = new Array(3);//new可省略
var names = new Array["Mia"];
数组尾部添加新项
colors[colors.length] = "pink"

js判断数组类型的方法
  • instanceof
var a=[];
console.log(a instanceof Array) //返回true 
  • constructor
    constructor 属性返回对创建此对象的数组函数的引用
console.log([].constructor == Array);
console.log({}.constructor == Object);
console.log("string".constructor == String);
console.log((123).constructor == Number);
console.log(true.constructor == Boolean);

较为严谨并且通用的方法:

function isArray(object){
    return object && typeof object==='object' &&
            Array == object.constructor;
}

!!注意:

使用instaceof和construcor,被判断的array必须是在当前页面声明的!比如,一个页面(父页面)有一个框架,框架中引用了一个页面(子页面),在子页面中声明了一个array,并将其赋值给父页面的一个变量,这时判断该变量,Array == object.constructor;会返回false;
原因:
1、array属于引用型数据,在传递过程中,仅仅是引用地址的传递。
2、每个页面的Array原生对象所引用的地址是不一样的,在子页面声明的array,所对应的构造函数,是子页面的Array对象;父页面来进行判断,使用的Array并不等于子页面的Array;切记,不然很难跟踪问题!

  • 特性判断法
function isArray(object){
    return  object && typeof object==='object' &&    
            typeof object.length==='number' &&  
            typeof object.splice==='function' &&    
             //判断length属性是否是可枚举的 对于数组 将得到false  
            !(object.propertyIsEnumerable('length'));
}

有length和splice并不一定是数组,因为可以为对象添加属性,而不能枚举length属性,才是最重要的判断因子。备注:如果 proName 存在于 object 中且可以使用一个 For…In 循环穷举出来,那么 propertyIsEnumerable 属性返回 true

  • 最简单的方法
function isArray(o) {
    return Object.prototype.toString.call(o) === ‘[object Array]‘;
}

转化方法

  • arrayObject.toLocaleString()
    返回值:
    arrayObject 的本地字符串表示。
  • toString()
    把数组转换为字符串,并返回结果。返回值与没有参数的 join() 方法返回的字符串相同。
    alert(colors.join(","))
  • valueOf
    valueOf()方法返回 Array 对象的原始值。实际上,为了创建这个字符串会调用数组的每一项的toString()方法。

栈方法

栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构ECMAScript为数组提供了push()和pop()方法,可以实现类似栈的行为。分别添加到数组末尾和从数组末尾移除最后一项。

队列方法

shift:从数组中把第一个元素删除,并返回这个元素的值。
unshift: 在数组的开头添加一个或更多元素,并返回新的长度
push:在数组的中末尾添加元素,并返回新的长度
pop:从数组中把最后一个元素删除,并返回这个元素的值。

unshift比push要慢差不多100倍!因此,平时还是要慎用unshift,特别是对大数组。那如果一定要达到unshift的效果,可以借助于Array的reverse方法,Array的reverse的方法能够把一个数组反转。先把要放进数组的元素用push添加,再执行一次reverse

重排序方法

reverse()——反转
sort()——升序排列数组

操作方法

  • concat()方法
    基于当前数组中所有项创建新数组。
var colors = ["red","green","blue"];
var colors2 = colors.concat("yellow",["black","brown"]);

alert(colors);
alert(colors2);

结果为colors为数组[“red”,”green”,”blue”];
colors2为数组[“red”,”green”,”blue”,”yellow”,”black”,”brown”];
concat()方法只是用当前数组重新创建一个新的数组,因此当前数组保持不变(colors数组不变

  • slice()方法
    slice()方法:基于当前数组中的一个或多个项创建一个新数组。
    slice()方法中可以有一个或者两个参数(代表数组的索引值,0,1,2……)。
    接收一个参数时:返回当前数组中从此参数位置开始到当前数组末尾间所有项。
    接收两个参数时:返回当前数组中两个参数位置间的所有项,但不返回第二个参数位置的项。
    参数也可以为负数,表示从末尾算起,-1代表最后一个,使用方法和正数一样。
var colors = ["red","green","blue","yellow","black","brown"];
var colors2 = colors.slice(2);
var colors3 = colors.slice(1,4);
var colors4 = colors.slice(2,-2);
var colors5 = colors.slice(-3,-1);

console.log(colors2);
console.log(colors3);
console.log(colors4);
console.log(colors5);

结果为:
[“blue”, “yellow”, “black”, “brown”]
[“green”, “blue”, “yellow”]
[“blue”, “yellow”]
[“yellow”, “black”]

  • splice()
    splice()主要用途是向当前数组的中间插入项,可以进行删除、插入、替换操作。会返回一个数组,包含从原始项中删除的项(若果没有删除,返回一个空数组)

删除:两个参数,删除起始项的位置和删除的项数。

var colors = ["red","green","blue"];
var removed = colors.splice(1,2);
alert(colors);      //red
alert(removed);     //green,blue

插入:在指定位置插入任意数量项,包括两个基本参数(即删除操作中的两个参数类型)和要插入项的参数,两个基本参数为起始位置和0(要删除的项数应为0项),要插入的项参数可以是任意个(”red”,”green”,”blue”)。

var colors = ["red","green","blue"];
var removed = colors.splice(1,0,"yellow","orange");
alert(colors);      //"red","yellow","orange","green","blue"
alert(removed);     //空数组

替换:向指定位置插入任意数量的项同时删除任意数量的项,插入项数和删除项数可以不同。参数包括两个基本参数(即删除操作中的两个参数类型)和要插入项的参数。

var colors = ["red","green","blue"];
var removed = colors.splice(1,1,"purple","black");
alert(colors);    //"red","purple","black","blue"
alert(removed);   //"green"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  

位置方法

  • indexOf() 方法
    返回某个指定的字符串值在字符串中首次出现的位置。

  • lastIndexOf() 方法
    返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。

  • substr
    substr(start,length)表示从start位置开始,截取length长度的字符串。
    var src="images/off_1.png";
    alert(src.substr(7,3));
    弹出值为:off

  • substring
    substring(start,end)表示从start到end之间的字符串,包括start位置的字符但是不包括end位置的字符。
    var src="images/off_1.png";
    alert(src.substring(7,10));
    弹出值为:off

迭代方法

var arr = [3,4,5,6,7,"a"];
var isNum = function(elem,index,AAA){
return !isNaN(elem);
}
var toUpperCase = function(elem){
return String.prototype.toUpperCase.apply(elem);
}
var print = function(elem,index){
console.log(index+"."+elem);
}
  • every
    对数组中的每一项执行测试函数,直到获得对指定的函数返回 false 的项。使用此方法 可确定数组中的所有项是否满足某一条件,类似于&&的含义
var res = arr.every(isNum);
console.log(res);//false;
  • some
    对数组中的每一项执行测试函数,直到获得返回 true 的项。 使用此方法确定数组中的所有项是否满足条件.类似于||的含义
res = arr.some(isNum);
console.log(res);//true
  • filter
    对数组中的每一项执行测试函数,并构造一个新数组,返回 true的项被添加进新数组。 如果某项返回 false,则新数组中将不包含此项
res = arr.filter(isNum);
console.log(res);//[3, 4, 5, 6, 7]
  • map
    对数组中的每一项执行函数并构造一个新数组,并将原始数组中的每一项的函数结添加进新数组。
res = arr.map(toUpperCase);
console.log(res);//["3", "4", "5", "6", "7", "A"]
  • forEach
    对数组中的每一项执行函数,不返回值
res = arr.forEach(print);
console.log(res);

缩小方法

  • reduce
array.reduce(callbackfn,[initialValue])
function callbackfn(preValue,curValue,index,array){}

**preValue
**: 上一次调用回调返回的值,或者是提供的初始值(initialValue)
**curValue
**: 数组中当前被处理的数组项
**index
**: 当前数组项在数组中的索引值
**array
**: 调用 reduce()

Date类型

Date.parse接受一个字符串参数,如果可以转化,将转换为对应的毫秒数,否则返回 NaN;
Date.UTC最少接受两个参数,分别表示年份和月份(0·11),其他的日期,小时(0-24)、分钟、秒,可以指定也可以不指定,不指定时默认为 0;

dateObject.getTime() 0~... 从GTM1970年1月1日0:00:00开始计算的毫秒数。
dateObject.getYear() 70~... 指定的年份减去1900,2000年后为4位数表示的年份。
dateObject.getFullYear() 1970~... 4位数年份,适用于版本4以上的浏览器。
dateObject.getMonth() 0~11 一年中的月份(1月为0)。
dateObject.getDate() 1~31 一月中的日期。
dateObject.getDay() 0~6 星期(星期日为0)。
dateObject.getHours() 0~23 一天中指定的小时数,采用24小时制。
dateObject.getMinutes() 0~59 指定小时内的分钟数。
dateObject.getSeconds() 0~59 指定分钟内的秒数。
dateObject.setTime(val) 0~... 从GTM1970年1月1日0:00:00开始计算的毫秒数。
dateObject.setYear(val) 70~... 指定的年份减去1900,2000年后为4位数表示的年份。
dateObject.setMonth(val) 0~11 一年中的月份(1月为0)。
dateObject.setDate(val) 1~31 一月中的日期。
dateObject.setDay(val) 0~6 星期(星期日为0)。
dateObject.setHours(val) 0~23 一天中的小时数,采用24小时值。
dateObject.setMinutes(val) 0~59 指定小时内的分钟数。
dateObject.setSecond(val) 0~59 指定小时内的秒数。

当前时间

var start = Date.now()

格式化

var d = new Date();
console.log(d); // 输出:Mon Nov 04 2013 21:50:33 GMT+0800 (中国标准时间)
console.log(d.toDateString()); // 日期字符串,输出:Mon Nov 04 2013
console.log(d.toGMTString()); // 格林威治时间,输出:Mon, 04 Nov 2013 14:03:05 GMT
console.log(d.toISOString()); // 国际标准组织(ISO)格式,输出:2013-11-04T14:03:05.420Z
console.log(d.toJSON()); // 输出:2013-11-04T14:03:05.420Z
console.log(d.toLocaleDateString()); // 转换为本地日期格式,视环境而定,输出:2013年11月4日
console.log(d.toLocaleString()); // 转换为本地日期和时间格式,视环境而定,输出:2013年11月4日 下午10:03:05
console.log(d.toLocaleTimeString()); // 转换为本地时间格式,视环境而定,输出:下午10:03:05
console.log(d.toString()); // 转换为字符串,输出:Mon Nov 04 2013 22:03:05 GMT+0800 (中国标准时间)
console.log(d.toTimeString()); // 转换为时间字符串,输出:22:03:05 GMT+0800 (中国标准时间)
console.log(d.toUTCString()); // 转换为世界时间,输出:Mon, 04 Nov 2013 14:03:05 GMT

RegExp

RegExp详谈

function

函数内部属性只要包括两个特殊的对象:arguments和this。
函数属性包括:length和prototype
函数方法(非继承)包括:apply()和call()
继承而来的函数方法:bind()、toString()、toLocaleString()、valueOf()

  • 没有重载
    js不存在重载的概念,后面的方法会覆盖先前的同名的方法。
  • 函数声明和函数表达式
    1)函数声明(Function Declaration);
    // 函数声明
    function funDeclaration(type){
        return type==="Declaration";
    }

2)函数表达式(Function Expression)。

    // 函数表达式
    var funExpression = function(type){
        return type==="Expression";
    }

上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个“陷阱”就体现在Javascript两种类型的函数定义上。下面看两段代码:

     funDeclaration("Declaration");//=> true
     function funDeclaration(type){
         return type==="Declaration";
     }
     funExpression("Expression");//=>error
     var funExpression = function(type){
         return type==="Expression";
     }

用函数声明创建的函数funDeclaration可以在funDeclaration定义之前就进行调用;而用函数表达式创建的funExpression函数不能在funExpression被赋值之前进行调用。
代码1段JS函数等同于:

    function funDeclaration(type){
        return type==="Declaration";
    }
    funDeclaration("Declaration");//=> true

代码2段JS函数等同于:

    var funExpression;
    funExpression("Expression");//==>error
    funExpression = function(type){
        return type==="Expression";
    }

再来个例子

var sayHello;
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        function sayHey() {
            console.log("sayHey");
        }
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        function sayHey() {
            console.log("sayHey2");
        }
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

等同于

var sayHello;
    function sayHey() {
            console.log("sayHey");
        }
    function sayHey() {
            console.log("sayHey2");
    }
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中。

  • 函数的内部属性
  • arguments
    有两个特殊的对象:arguments和this。其中,arguments它是一个类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象含所有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
    下面这个非常经典的阶乘函数。
function factorial (num){
  if(num <= 1){
    return 1;
  } else{
  return num * factorial(num-1); 
  }
}

定义阶乘函数一般都会用到递归算法,如上面代码所示,在有函数名字,并且函数名字以后也不会改变的情况下,这种定义没问题。但是这个函数的执行与函数名factorial紧紧耦合在了一起,为了消除这种紧密耦合现象(函数名字改变等情况),可以使用arguments.callee。

function factorial(num){
if(num<=1){
  return 1;
  } else{
    return num * arguments.callee(num-1);
  }
}

在这个重写后的factorial()函数的函数体内,没有再引用函数名factorial。这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。例如:

var trueFactorial=factorial;
factorial=function()
{
    return 0;
};
 
alert(trueFactorial(5));//120
alert(factorial(5));//0

在此,变量trueFactorial获得了factorial的值,实际上是另一个位置上保存了一个函数的指针。然后,我们又将一个简单的返回了0的函数赋值给了factorial变量。如果像原来factorial()那样不用使用arguments.callee,调用trueFactorial()就会返回0。可是,在解除了函数体内的代码与函数名的耦合状态之后,trueFactorial()仍然能够正常的计算阶乘;至于factorial(),它现在只是一个返回0的函数。

  • this
     函数内部的另一个特殊对象是this,其行为与java和c#中的this大致类似。换句话说,this引用的是函数据以执行的环境对象,或则也可以说是this值(当在网页的全局作用中调用函数时,this对象引用的就是window)。如下例子:
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
sayColor();//red
 
o.sayColor=sayColor;
o.sayColor();//blue
  • apply()方法
    接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以使Array的实例,也可以是arguments对象
unction sum(num1,num2)
{
    return num1+num2;
}
 
function callSum1(num1,num2)
{
    return sum.apply(this,arguments);//传入arguments对象
}
 
function callSum2(num1,num2)
{
    return sum.apply(this,[num1,num2]);//传入数组
}
 
alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
  • call()方法
    对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话讲,在使用call()方法时,传递给函数的参数必须逐个列举出来,如下例子:
function sum(num1,num2)
{
    return num1+num2;
}
 
function callSum(num1,num2)
{
    return sum.call(this,num1,num2);
}
 
alert(callSum(10,10));//20

事实上,传递参数并非apply()和call()正真的用武之地;它们正真强大的地方是能够扩充函数赖以运行的作用域。如下例子:

window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
sayColor();//red
 
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(o);//blue

这一次,sayColor()也是作为全局函数定义的,而且当在全局作用域中调用它时,它确实会显示“red”,因为对this.color的求值会转换成对window.color的求值。而sayColor.call(this)和sayColor.call(window),则是两种显示的在全局作用域中调用函数的方式,结果当然都会显示“red”。但是,当运行sayColor.call(o)时,函数的执行环境就不一样了,因为此时函数体内的this对象指向了o,于是结果显示的是“blue”。
使用call()或apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。在前面例子的第一个版本中,我们是先将sayColor()函数放到了对象o中,然后再通过o来调用它;而在这里重写的例子中,就不需要先前那个多余的步骤了。

  • bind()
window.color="red";
var o={color:"blue"};
 
function sayColor()
{
    alert(this.color);
}
 
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue

在这里,sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor()函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会看到“blue”。

函数表达式

理解作用域链是理解闭包的关键;

(1)什么是执行环境

执行环境有二种: 全局执行环境;局部执行环境---function里面;

执行流进入函数执行时,创建执行环境;函数执行结束,回收!

(2)变量对象

理解变量对象非常重要,变量对象在函数中也称为 活动对象,我还是喜欢说成变量对象。

每一个执行环境都有一个变量对象,变量对象会保存这个执行环境的变量和方法;我们不难想到全局的变量对象是谁? window对象

我们在全局中添加变量和方法都是添加到window里。函数执行时 执行环境中就会创建变量对象;一般来说:函数调用完毕之后无论是执行环境(出栈),还是变量对象都是回收的。闭包的出现使得变量对象不回收;

(3)作用域链?

什么是作用域链:作用域链的本质是 指向变量对象的一组有序指针;字面上很难理解,我第一次看不明白!

有什么作用:在一个执行环境中对变量和方法进行有序(正确)访问;

什么时候创建: 一个函数声明的时候就创建了,作用域链会保存在一个函数的内部属性[[Scope]]中;

注意:执行流进入函数是: 创建执行环境->将作用域链(利用[[Scope]]属性) 复制到执行环境中->创建变量对象(活动对象)->将变量对象的引用(指针)导入作用域链的最前端->执行代码

具体请看下面的代码

  function compare(value1,value2){
         if(value1<value2){return 1;}
          else if(value1>value2){return -1;}
          else{return 0;}
       }
      var result=compare(5,10)//1

我们可以看到,作用域链是什么:有序指针,前面说的作用域最前端在就是0位置。 查找变量或者函数是最前端开始的,0指向的活动对象没有你要找的变量,再去1找。0-1其实

从代码的角度上看其实就是从函数内部一直向函数外部找标识符(变量或者函数),找到立即停止,不会再向上查找。这就是作用域链!

  • 闭包
    定义: 闭包是有权访问另外一个函数作用域变量的函数;注意闭包是一个函数!
    创建闭包的主要的方法:在一个函数里面创建一个函数(闭包); 比如
<script type="text/javascript">
    function A(value){
     var num=value;
    return function(){ //闭包
     alert(num);
  }
}
var B = A(0);
alert(B()); //0
</script>

在这里匿名函数是一个闭包,一般情况下: 一个函数执行完毕之后,无论是作用域链还是变量对象都会回收,但是这个匿名函数的作用域链里有A()变量对象的引用,所以没有消除。

还可以访问变量对象的num;这就是闭包的好处!但是闭包的出现加大内存负担,就这里而已,我们即使后面不再使用B函数了,A()变量对象也不会消失,javascript不知道你什么时候还会再用,当然我们可以这样 B=null; 这样的话匿名函数没有引用,被回收,A()变量对象也一起回收!

《javascript高级程序设计》中尼古拉斯大神建议我们:可以不使用闭包尽量不使用,万不得已才使用!

  • 块级作用域

    我们都知道javascript是没有块级作用域的,比如{}; if(i=0){} while(){} for(){}这些都不会形成块级作用域; 那么怎么创建 java c#类似功能的块级作用域?

    语法:

(function(){

//块级作用域

})();

注意: 我们创建一个匿名函数,立即调用,里面的代码都要运行一遍,而在window中看不见,这不就是块级作用域吗? 还有一个好处,这个匿名函数没有指针,

调用后回收,不会产生垃圾(里面的方法,变量都不需要再访问的)。简直就是完美的块级作用域! 注意格式 (匿名函数)其实就是一个指针,再加上()就是调用了。

  • 构造函数中的闭包

    (1) 我们知道怎么为对象添加'私有变量' 这样

function Person(name,age){
    this.name=name;//共有属性
    this.age=age;
    
    var school="一中"; //私有属性
    this.GetSchool=function (){return school;}
}

我们这个school是私有的变量,因为闭包的原因, 对象实例自然可以访问这个变量; 比如 var p=new Person('nos',20); p.GetSchool(); // 一中;

 (2)现在来一个奇葩: 静态私有属性 ;
(function(){

         var school=""; //静态私有;

        Person=function(name,age,value){ //构造函数

         this.name=name;this.age=age;

          school=value;

     }; 

    Person.prototype.GetSchool=function(){alert(school);}

         })();
      var p=new Person('andy',21,'一中');
      p.GetSchool();//一中
      var pp=new Person('nos',29,'二中');
      pp.GetSchool();//二中
      p.GetSchool();//二中

从结果上看 school是对象共有的,私有的属性, 即静态私有属性;
我们看看构造函数是怎么定义的: 没有使用var ,前面说过即使在函数里面定义,没有使用var申明,就是window的,为全局的。自然可以在全局使用!

  这个匿名函数中调用了一次,只产生一个对象变量,school都是同一个,实现共享。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容