ES5 核心

作用域

在 JavaScript 中, 作用域(scope,或译有效范围)就是变量和函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。在ES5中没有块级作用域的概念。

  • 全局作用域(Global Scope):最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的。
  • 局部作用域(Local Scope):和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部。
  • 如果局部作用域的“预解析空间”(AO-活动对象(Active object))没有找到,那么代码会从上一级的作用域寻找,上级作用域不能在下级作用域寻找。

注意事项:

  1. 需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
  2. 只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”

作用域链(Scope Chain)

通俗地讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。当执行函数时,总是先从函数内部找寻局部变量;如果内部找不到(函数的局部作用域没有),则会向创建函数的作用域(声明函数的作用域)寻找,依次向上。

  • 执行环境(execution context):JavaScript为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。

闭包

  1. 可以读取自身函数外部的变量(沿着作用域链寻找)
  2. 让这些外部变量始终保存在内存中

js函数内的变量值不是在编译的时候就确定的,而是等在运行时期再去寻找的。

模块

  1. 必须有外部的封闭函数,该函数至少被调用一次(每次调用都会创建一个新的模块实例)。
  2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改其他私有的状态。

一个具有函数属性的对象本身并不是真正的模块。从方便观察的角度看,一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并不是真正的模块。

this对象

“谁调用指向谁”

this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。不过,匿名函数具有全局性,因此this对象同常指向window。

严格模式

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
  2. 消除代码运行的一些不安全之处,保证代码运行的安全
  3. 为未来新版本的Javascript做好铺垫

在使用严格模式的时候在全局或函数的第一条语句需定义为: ‘use strict’; 如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用

主要内容:

  1. 必须用var声明变量(混杂模式中可以直接使用变量而不定义,但是这种做法会对后期造成很大的麻烦)
  2. 创建eval作用域
  3. 禁止this指向window
  4. 对象不能用重名的属性
  5. 函数不能有重名的形参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'use strict'; //在全局或函数内第一行书写
//1.变量必须使用var 定义 否则报错
str="xxx";
//2.函数内的this指向不再是window
function Person(name,age) {
console.log(this);//undefined
this.name=name;//相当于设置undefined.name =name 报错
this.age=age;
}
//3.让eval有自己的作用域
var str =123;
eval('var str=456;alert(str)');//456
alert(str);//123
//4.对象不能用重名属性
var obj ={
username:'kobe',
username:'zs'
}
console.log(obj);//运行时没有报错,不过编辑器内有提示 最好别这么写就是了

json对象

JSON.stringify(obj/arr)js对象(数组)转换为json对象(数组)
JSON.parse(json)json对象(数组)转换为js对象(数组)

1
2
3
4
5
var obj={username:"mike"};
obj=JSON.stringify(obj);
console.log(typeof obj);
//json对象和数组,不要说json字符串
//json是一种传输数据的格式,还有xml。

object对象方法扩展

ES5给Object扩展了好一些静态方法, 常用的2个:

  • Object.create(prototype[, descriptors]) : 创建一个新的对象
  1. 以指定对象(prototype)为原型创建新的对象
  2. 指定新的属性, 并对属性进行描述
    value : 指定值
    writable : 标识当前属性值是否是可修改的, 默认为true
    get : 用来得到当前属性值的回调函数
    set : 用来监视当前属性值变化的回调函数
1
2
3
4
var obj={username:"damu",age:30};
var obj1={};
obj1=Object.create(obj); //Object是obj1上一级的构造函数,调用他所具有的函数creat
console.log(obj1); //也就是obj1可以继承obj的属性username和age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var obj={username:"damu",age:30};
var obj1={};
obj1=Object.create(obj,{
sex:{
value:"男",
writable:true,//有一个writable属性:标识当前属性值是否是可以修改的,默认为false
configurable:true,//有一个属性configurable:标识当前属性是否可以被删除
enumerable:true //有一个属性enumerable:标识当前属性是否能用for in枚举 默认为false
}
} //对当前扩展属性的描述
}); //使用create给obj1添加扩展属性
console.log(obj1.sex);
obj1.sex="女"
console.log(obj1.sex);
delete obj1.sex;//规定不能直接删掉,有一个属性configurable:标识当前属性是否可以被删除,默认为false
console.log(obj1.sex);
for(var i in obj1){
console.log(i)//for in 找不到obj1使用create扩展出来的属性
//有一个属性enumerable:标识当前属性是否能用for in枚举 默认为false
}
  • Object.defineProperties(object,descriptors)
    作用:为指定object对象定义扩展多个属性
    get:用来获取当前属性值的回调函数
    set:修改当前属性值的触发的回调函数,并且实参即为修改后的值
    存取器属性:setter,getter一个用来存值一个用来取值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var obj2={firstName:"kobe",lastName:"bryant"};
Object.defineProperties(obj2,{
fullName:{
get:function () {//使用get来获取扩展属性的值
return this.firstName+" "+this.lastName//fullName想要设置的值
},
set:function (data) {//监听扩展属性,当扩展属性发生变化时自动调用后会将变化的值作为实参注入到set函数。
console.log("set()",data);
var names=data.split(" ");
this.firstName=names[0];
this.lastName=names[1];
}
}//创建一个配置对象
})

console.log(obj2.fullName)
obj2.fullName="haha heih";
console.log(obj2.fullName)
//get方法什么时候调用? 获取扩展属性值的时候,get方法自动调用

//对象本身的两个方法:
//get propertyName(){}
//set propertyName(){}

var objj={
age:15,
tall:180,
get all(){
return this.age+" "+this.tall;
},
set all(data){
console.log("set()",data);
var names=data.split(" ");
this.age=names[0];
this.tall=names[1];
}

}
console.log(objj);
objj.all="13 189";
console.log(objj);

数组扩展

  1. Array.prototype.indexOf(value):得到值在数组中的第一个下标,输入第一个3的下标
  2. Array.prototype.lastIndexOf(value):得到值在数组中的最后一个下标
  3. Array.prototype.forEach(function(item,index){}):遍历数组
  4. Array.prototype.map(function(item,index){}):遍历数组返回一个新的数组,返回加工之后的值
  5. Array.prototype.filter(function(item,index){}):遍历过滤出一个新的子数组,返回条件为true的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var arr=[2,3,5,1,4,6,3];
console.log(arr.indexOf(3)); //输出第一个6的下标
console.log(arr.lastIndexOf(3)); //输出最后一个6的下标

arr.forEach(function(item,index){
console.log(item+" "+index) //输出所有元素的值和下标
})

var arr1=arr.map(function(item,index){
return item+10;
});
console.log(arr1); //根据arr产生一个新数组,要求每个元素比原来大10

var arr2=arr.filter(function(item,index){
return item > 4;
})

console.log(arr2); //根据arr产生一个新数组,返回的每个元素都要大于4

bind()、call()、apply()的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var obj={username:"kobe"};
function foo(data){
console.log(this,data)
}
// foo();
//自调用时this指向的是window,使this指向我们定义的obj
foo.call(obj);
foo.apply(obj);
//call和apply在不传参的情况下使用方式是一样的

//区别
function foo(data){
console.log(this,data)
}

foo.call(obj,33); //直接传入参数
foo.apply(obj,[33]); //传入数据必须写在数组里

//bind

var bar=foo.bind(obj,33);//绑定完this有一个返回值,不会立即调用当前函数而是将函数返回,通常用来指定回调函数的this。用bar来接收返回的函数然后执行
bar();

// 也可写成foo.bind(obj,33)()