console.log(a) // undefined
var a = 1
console.log(b) // 报错
b = 2
当一段 JavaScript 执行时,要先经历一个解析阶段,就是把要用的变量放入全局的执行上下文,比如解析到 var a 时,会将 a 扔到全局执行上下文中。
开始执行代码,当用到 a 变量时,a 还未被赋值,被得到 undefined。
b 未使用 var 关键字来定义,所以不会放到全局执行上下文中,当用到 b 时,会报错,b 未定义。
函数也有执行上下文,比全局执行上下文多 thisarguments
闭包
闭包指的是,有权限访问访问其他作用域中变量的函数。
理解闭包核心:JavaScript的函数作用域是在函数创建的时候定义的,而不是在执行的时候创建的
看下面的例子:
function out() {
var name = "js"
return function inner(){
console.log(name)
}
}
var fn = out() // fn 引用了 out 的作用域
fn() // 当 fn 执行的时候就可以访问到 out 的作用域中的 name 变量
function func(resolve, reject){
setTimeout(function(){
resolve('func')
},500)
}
new Promise(func)
.then(function(data){
console.log(data)
return 'next'
})
.then(function(data){
console.log(data)
})
Promise 解决了代码横向发展的问题,但一眼望去全是 .then,可读性很差。
所以又出现了 generator 来解决这个问题:
generator 基础应用:
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
var g = gen();
var result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
});
变量类型
基本类型:
Null
Undefined
String
Number
Boolean
Symbol
引用类型:
Object
Function
判断类型的方法:type of
判断实例与构造函数依赖的方法:instanceof
原型和原型链
在 JavaScript 中万物皆对象。Null 是所有对象的源头,Null就像上帝,Function 和 Object 就是亚当和夏娃,它们通过 prototype 和 constructor 繁衍后代,prototype 提供基因,constructor 就是子宫。
__proto__
属性。__proto__
都指向它的构造函数的 prototype 属性。用代码来描述:
当对象
obj
在访问属性的时候,会在自身属性中寻找,如果自身没有,就会通过obj.__proto__
(即构造函数的prototype
)中去寻找。如果还没有就会继续通过obj.__proto__.__proto__
中去寻找,一直向上寻找,就形成了原型链。如果在最上层也没找到,就会返回undefined
。最上层是什么 ——
Object.prototype.__proto__ === null
作用域和闭包
作用域
作用域的目的是确定变量的访问权限,也就是为了避免变量污染。
首先 JavaScript 采用的是词法作用域,也就是静态作用域,即创建的时候就确定好了。
在ES6以前,JavaScript 只有全局作用域和函数作用域,没有块级作用域,不像 Java 那样,一个花括号就是一个作用域。
在ES6中 引入了 let 关键字,才有了块级作用域。
作用域链
当变量查找时,会从当前上下文中的变量对象中查找,如果没有,会从父级上下文的变量对象中查找,直到找到全局的变量。由多个执行上下文的变量对象构成的链表就叫作用域链
执行上下文
当一段 JavaScript 执行时,要先经历一个解析阶段,就是把要用的变量放入全局的执行上下文,比如解析到
var a
时,会将 a 扔到全局执行上下文中。开始执行代码,当用到 a 变量时,a 还未被赋值,被得到 undefined。
b 未使用 var 关键字来定义,所以不会放到全局执行上下文中,当用到 b 时,会报错,b 未定义。
函数也有执行上下文,比全局执行上下文多
this
arguments
闭包
闭包指的是,有权限访问访问其他作用域中变量的函数。
理解闭包核心:JavaScript的函数作用域是在函数创建的时候定义的,而不是在执行的时候创建的
看下面的例子:
垃圾回收机制
引用计数
当声明一个变量,将一个引用类型赋值给该变量,则当前引用次数为1,如果同一个值赋给另一个变量,则引用次数加1。相反,如果拥有该值的变量拥有了其他值则减1,当值的引用次数为0时,垃圾回收期下次运行时,就会回收。
下面这个例子:
标记清除
当函数中声明一个变量,则标记这个变量为“进入环境”,只要进入相应的环境你就可以访问到它。当变量离开环境时,则将其标记会“离开环境”
异步
由于回调函数的写法会导致,函数嵌套,造成代码横向发展,而不是纵向发展,从而难看难维护。
Promise 应运而生。
基本用法:
Promise 解决了代码横向发展的问题,但一眼望去全是 .then,可读性很差。
所以又出现了 generator 来解决这个问题:
generator 基础应用:
generator 是 ES6 对协程的实现,yield 可以暂停函数。generator 需要手动通过 .next() 来启动函数继续执行,加上 * 和 yield 让人很难理解。
async/await 目前来说终极的解决方案:
基本用法:
async/await 其实就是 generator 的语法糖,* 号相当于 async,yield 相当于 await。await 有了返回值后会继续执行不需要像 generator 需要通过 .next 来启动函数继续执行
ES6/7常用特性