20180812
变量提升
- 提升是浏览器解析JavaScript的结果。在执行JavaScript代码前,所有变量都会被“提升”,提升到函数作用域的顶部。
function getClothing(isCold){
if(isCold){
var freezing = "Grab a jacket!";
} else{
var hot = "It's a shorts kind of day.";
console.log(freezing)
}
}
变量提升后相当于
function getClothing(isCold){
var freezing,hot;
......
}
常量
- ES3:无常量概念
- ES5:给对象定义属性,绑定在window上,并设属性为只读 Object.defineProperty()
- ES6:const定义
//ES5
Object.defineProperty(window, "PI5", {
value: 3.1415926,
writable: false,
})
console.log(window.PI5);
//ES6
const PI6 = 3.1415926;
console.log(PI6);
变量声明
- var:要么为全局作用域,要么为本地作用域,也就是整个函数作用域。
- let:作用域为块。变量可以重新赋值,但是不能在同一作用域内重新声明。
- const:作用域为块。变量必须赋初始值,但是不能在同一作用域内重新声明,也无法重新赋值。
if(isCold){
let freezing = 'Grab a jacket!';
} else{
let hot = 'Its a shorts kind of day';
console.log(freezing); // not defined
}
- 使用 let 和 const 声明的变量仅在它们所声明的块中可用.
- 如果在代码块(用花括号 { } 表示)中使用 let 或 const 声明变量,那么该变量会陷入暂时性死区,直到该变量的声明被处理。这种行为会阻止变量被访问,除非它们被声明了。
箭头函数
- 箭头函数与普通函数区别在于this的绑定。
- ES3\ES5:function a(){}
- ES6:(parameters)=>{statements}; ()中参数,若只有一个参数,省略()。 {}中表达式直接作为返回值时省略。
——————————————————————–
数组遍历evens.map()
-ES3\ES5:使用evens.map(function(){})
-ES6:使用箭头函数
{
//ES3\ES5
var evens = [1, 2, 3, 4, 5];
var odds = evens.map(function(v) {
return v + 1
});
console.log(evens, odds);
};
{
//ES6
let evens = [1, 2, 3, 4, 5];
let odds = evens.map(v => v + 1); //箭头函数
console.log(evens, odds);
};
——————————————————————–
this指向问题
普通函数:this的指向是该函数被调用的对象。
箭头函数:this指向的是定义时this的指向。
普通函数:
- this总是代表它的直接调用者(js的this是执行上下文),例如 obj.func ,那么func中的this就是obj;
- 在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window;
- 在严格模式下,没有直接调用者的函数中的this是undefined;
- 使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象;
箭头函数:
- 箭头函数没有自己的this,它的this是继承而来;
- 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象。 定义它的时候,可能环境是window;
- 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this;
{
//ES3\ES5声明一个类,用函数作为类构造器.
var factory = function(){
this.a = 'a';
this.b = 'b';
this.c = {
a:'a+',
b:function(){ //普通函数声明
return this.a //this的指向是该函数被调用的对象,b()由c调用
}
}
}
};
使用 new factory().c.b()
访问c中b函数。b()由c调用,故b()函数体中this指向c。
{
// ES6使用箭头函数,避免this指向不确定
let factory=function(){ //构造函数中this指向factory的实例
this.a = 'a';
this.b = 'b';
this.c = {
a:'a+',
b:()=>{ //箭头函数声明,箭头函数this指向是定义时this的指向。b在定义这个函数时this指向factory实例
return this.a
}
}
}
}
使用new factory().c.b()
访问。因b使用箭头函数声明,故this指向:定义时this的指向(即指向factory实例)。
作用域
- 函数名与执行函数
- 函数名只是一个标识(指向函数的指针),而()才是执行函数。
- 当一个函数被调用完成之后,其执行上下文环境将被销毁,其中的变量也会被同时销毁。
//ES5
const callbacks = []
for (var i = 0; i <= 2; i++) { // var 变量提升
callbacks[i] = function() { //没有执行函数,函数内部不变,函数体内保存表达式而非值,形成闭包。
return i * 2 //对变量的引用,而非对值的引用。 所以其「值」只有在执行时才能确定
}
}
//内存回收机制:当一个函数被调用完成之后,其执行上下文环境将被销毁,其中的变量也会被同时销毁。垃圾回收callbacks[]。
//暂不回收i(变量被引用着所以不会被回收)
//数据以表格的形式显示,接收一个强制的参数(必须是一个数组或者是一个对象)
//执行return i*2,表达式求值i=3
console.table([
callbacks[0](),
callbacks[1](),
callbacks[2](),
- for中
callbacks[i]=function(){}
语句执行时 不能执行函数体(函数带()才是执行函数)。函数内部不变,函数体内保存表达式而非值,形成闭包。 callbacks[x]()
相当于执行return i * 2(此时 i==3)。- for结束后,callbacks[i]使用完成,由内存回收机制回收;而i不被回收(变量被callbacksx引用着所以不会被回收)。
——————————————————————–
- 通过使用let声明的变量,保存当前块作用域的值。
const callbacks2 = []
//let声明的变量,块作用域。 每循环一次,生成一个新的作用域
for (let j = 0; j <= 2; j++) {
callbacks2[j] = function() {
return j * 2 //该处闭包,取决于当前的块作用域,保存当前块作用域的值,供后面使用
}
}
console.table([
callbacks2[0](),
callbacks2[1](),
callbacks2[2](),
]);
——————————————————————–
- 作用域链
- 全局变量默认挂载在window对象下。
- 当在函数中使用变量时,首先在本函数内部查找该变量,后找其父级函数,最后直到window。
- 常见的window的属性和方法有: alert, location, document, parseInt, setTimeout, setInterval等, window的属性默认可以省略window前缀。
- 作用域隔离
ES3\ES5:通过‘立即执行函数’。
//ES3\ES5
((function(){
var foo = function(){
return 1
}
console.log("foo()===1",foo()==1);
((function(){
var foo = function(){ //与函数体外foo不冲突
return 2
}
console.log("foo()===2",foo()==2)
})());
console.log("foo()===1",foo()==1);
})());
ES6:使用{}指定作用域,隔离作用域。
{
function foo(){
return 1
}
console.log("foo()===1",foo()==1);
{
function foo(){
return 2
}
console.log("foo()===2",foo()==2);
}
console.log("foo()===1",foo()==1);
}
默认参数
基本使用
- ES3\ES5:x=x||1 判断是否为undefined
- ES6:function f(x,y=7,z=42){}
{
//ES3\ES5:默认参数写法
function f(x,y){
/*if(x===undefined){
x=7;
}
if(y===undefined){
y=42;
}*/
x = x || 7;
y = y || 42;
return x + y;
}
console.log(f(1,3));
};
{
// ES6
function f(x,y = 7,z = 42){
return x + y + z;
}
console.log(f(1));
};
必选参数检查:通过函数checkParameter()抛出异常throw new Error();并通过try…catch捕获异常。
function checkParameter(){
throw new Error('can\'t be empty');
}
function f(x = checkParameter(), y = 7, z = 42){
return x + y + z;
}
try{
f()
}catch(e){
console.log(e)
}finally{
}
——————————————————————–
可变参数
ES3\ES5 将参数数组化:Array.prototype.slice.call(arguments)
//ES3\ES5
function f(x){
var a = Array.prototype.slice.call(arguments); //arguments伪数组,通过Array.prototype.slice.call()转化为数组
var sum = 0;
a.forEach(function(item){
sum += item * 1;
})
return sum;
}
console.log(f(1,2,3));
ES6:function f(…a){}; …a扩展运算符,a为可变参数列表数组
//ES6
function f(...a){ //...a扩展运算符,a为可变参数列表数组
var sum=0;
a.forEach(item=>{
sum+=item*1 //转换为数字
});
return sum
}
console.log(f(2,3,4));
——————————————————————–
合并数组
ES3\ES5:.concat() 拼接数组
ES6:利用扩展运算符 var other=[1,2,…params];
{
//ES5 合并数组
var params=['hello',true,7];
var other=[1,2].concat(params);
console.log(other);
};{
//ES6 利用扩展运算符合并数组
var params=['hello',true,7];
var other=[
1,2,...params
];
console.log(other);
}
对象代理
ES3\ES5:var Person = function(){}; 内部声明局部作用域,通过this.get = function(){}和this.set = function(key, value){}访问内部。
//ES3\ES5 数据保护
var Person = function() { //构造函数
//内部声明,局部作用域。 若无this访问,则取不到data
var data = {
name: 'es3',
sex: 'male',
age: 15
}
this.get = function(key) {
return data[key];
}
this.set = function(key, value) {
if (key !== 'sex') {
data[key] = value;
}
}
}
//声明一个实例
var person = new Person();
//读取:通过api方式
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')});
//修改
person.set('name', 'es3-cname');
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')});
person.set('sex', 'female'); //数据保护
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')
});
ES5 直接声明对象: Object.defineProperty(Person, ‘sex’, {writable: false,value: ‘male’}); 设置只能读不能写。
//ES5 直接声明对象,而不用构造函数
var Person = {
name: 'es5',
age: 15
};
//保护数据:设置只能读不能写
Object.defineProperty(Person, 'sex', {
writable: false,
value: 'male'
});
console.table({name: Person.name, sex: Person.sex, age: Person.age});
Person.name = 'es5-cname';
console.table({name: Person.name, sex: Person.sex, age: Person.age});
try {
//不能为只读属性赋值
Person.sex = 'female';
console.table({name: Person.name, sex: Person.sex, age: Person.age});
} catch (e) {
console.log(e);
} finally {}
ES6:let person = new Proxy(Person, {}); 以Proxy作为Person代理。person作为用户操作对象,保护Person。
//ES6
let Person = {
name: 'es6',
sex: 'male',
age: 15
};
//Proxy作为Person代理。person作为用户操作对象,保护Person。
//target:Person;key:读取的属性
let person = new Proxy(Person, {
get(target, key) {
return target[key]
},
set(target, key, value) {
if (key != 'sex') {
target[key] = value;
}
}
});
console.table({name: person.name, sex: person.sex, age: person.age});
//对代理对象person操作
try {
person.sex = 'female';
} catch (e) {
console.log(e);
} finally {}