JS基础知识
- js的运行环境
1.全局环境(JS代码加载完毕后,进入代码预编译即进入全局环境)
2.函数环境(函数调用执行时,进入该函数环境,不同的函数则函数环境不同)
3.eval(不建议使用,会有安全,性能等问题)
每进入一个不同的运行环境都会创建一个相应的执行上下文(Execution Context),那么在一段JS程序中一般都会创建多个执行上下文,js引擎会以栈的方式对这些执行上下文进行处理,形成函数调用栈(call stack),栈底永远是全局执行上下文(Global Execution Context),栈顶则永远是当前执行上下文。 - 函数调用栈
首次运行JS代码时,会创建一个全局执行上下文并Push到当前的执行栈中。每当发生函数调用,引擎都会为该函数创建一个新的函数执行上下文并Push到当前执行栈的栈顶。当栈顶函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文控制权将移到当前执行栈的下一个执行上下文。
- 执行上下文的创建
- 创建变量对象(Variable Object)
- 建立作用域链(Scope Chain):变量对象(Variable Object)转为活动对象(Active Object)
var num = 30;
function test() {
var a = 10;
function innerTest() {
var b = 20;
return a + b
}
innerTest()
}
test()
//innerTest的调用链
innerTestEC = {
//变量对象
VO: {b: undefined},
//作用域链
scopeChain: [VO(innerTest), AO(test), AO(global)],
//this指向
this: window
}
- 确定this的指向
- 永远只有JS引擎线程在执行JS脚本程序
- 宏任务
- 同步任务指的是在JS引擎主线程上按顺序执行的任务,只有前一个任务执行完毕后,才能执行后一个任务,形成一个执行栈(函数调用栈)。
- 异步任务指的是不直接进入JS引擎主线程,而是满足触发条件时,相关的线程将该异步任务推进任务队列(task queue),等待JS引擎主线程上的任务执行完毕,空闲时读取执行的任务,例如异步Ajax,DOM事件,setTimeout等
- 事件循环
- 主线程执行栈
- 异步任务等待触发
- 任务队列
- 微任务:微任务(micro-task)的API主要有:Promise, process.nextTick
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start
// script end
// promise1
// promise2
// setTimeout
在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined(在 var 的情况下)或保持未初始化(在 let 和 const 的情况下)。所以这就是为什么可以在声明之前访问 var 定义的变量(尽管是 undefined ),但如果在声明之前访问 let 和 const 定义的变量就会提示引用错误的原因。此时let 和 const处于未初始化状态不能使用,只有进入执行阶段,变量对象中的变量属性进行赋值后,变量对象(Variable Object)转为活动对象(Active Object)后,let和const才能进行访问
- 在函数内部定义新函数
- 新函数访问外层函数的局部变量,即访问外层函数环境的活动对象属性
- 新函数执行,创建新的函数执行上下文,外层函数即为闭包
//对象的解构赋值
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
//字符串的解构赋值
const [a, b, c, d, e] = 'hello';
每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止 1、每次执行next(),都会执行一个yield返回的值 2、yield表达式本身没有返回值,或者说总是返回undefined(由next返回) 3、next()可无限调用,但既定循环完成之后总是返回undeinded
function* myYield2(x) {
let y = 2*(yield (x+1));
let z = yield (y+3);
return (x+y+z)
}
var num = myYield2(3)
console.log(num.next())//{value: 4, done: false}
console.log(num.next(2))//{value: 7, done: false}
console.log(num.next(2))//{value: 9, done: false}
console.log(num.next(2))//{value: undefined, done: true}
- 1.var num = myYield2(3) 执行了初始化,x为3
- 2.执行【第一个next()】,相当于执行了yield (x+1) 而x为3,所以返回value为4 传参不传参都无所谓,因为传的参数只是代替上面yield的值,第一个之前没有其他的yield
- 3.执行【第二个next(2)】,相当于let y = 2*(2); 结果y=4;yield (y+3);相当于yield (4+3) 最终结果返回7 执行【第三个next(2)】,相当于执行了let y = 2*(2);let z = 2;return (3+4+2);最终结果return也可以认为是一个yield
- 4.【第四个next(2)】,已经没有yield了,所以返回undefined
Promise对象就是一个容器,里面保存着某个未来才会结束的事件结果,它有两个特点 1、对象的状态不受外界的影响 2、一旦状态改变,就不会再变,任何时候都可以得到这个结果
//Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
//promise实例生成后,可以通过then方法分别指定resolved状态和rejected状态的回调函数,其中第二个函数可以不提供
promise.then(function(value) {
// success
}, function(error) {
// failure
});
var person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var person1 = {
firstName:"Bill",
lastName: "Gates",
}
var person2 = {
firstName:"Steve",
lastName: "Jobs",
}
person.fullName.call(person1); // 分别接受参数,将返回 "Bill Gates"
person.fullName.apply(person1); // 接受参数数组,将返回 "Bill Gates"
为了解决promise调用时相互依赖导致代码难以理解问题
x => x * x
//等价于
function (x) {
return x * x;
}