//generator yield 解析 function *test(){ console.log('a'); let data1 = yield 22; console.log(data1); return 'done' } var obj = test(); var obj1 = obj.next() //此时控制台打印'a',并给obj1 => {done: false, value: 22} var obj2 = obj.next('in') //此时控制台打印'in',并给obj2 => {done: true, value: "done"} //es7 提供了async await 和提供同步方式编写代码 //大概实现原理 function runner(_gen){ return new Promise((resolve, reject)=>{ var gen=_gen(); _next(); function _next(_last_res){ var res=gen.next(_last_res); if(!res.done){ var obj=res.value; if(obj.then){ obj.then((res)=>{ _next(res); }, (err)=>{ reject(err); }); }else if(typeof obj=='function'){ if(obj.constructor.toString().startsWith('function GeneratorFunction()')){ runner(obj).then(res=>_next(res), reject); }else{ _next(obj()); } }else{ _next(obj); } }else{ resolve(res.value); } } }); } //使用 runner(function *(){ alert('欢迎哈'); let arr=yield $.ajax({url: 'data/1.txt', dataType: 'json'}); alert('接收到了数组'+arr); let json=yield $.ajax({url: 'data/2.txt', dataType: 'json'}); alert('json也读完了'); console.log(json); });
//运算优先级 var a = {n: 1}; var b = a; a.x = a = {n: 2}; a.x // undefined b.x // {n:2} //js的赋值运算顺序永远都是从右往左的,不过由于“.”是优先级最高的运算符,所以这行代码先“计算”了a.x,所以对象A多了个值为undefined的x //这里对象A指{n: 1}, 对象B指{n: 2} //由于( . 运算符最先计算)一开始js已经先计算了a.x,便已经解析了这个a.x是对象A的x,所以在同一条公式的情况下再回来给a.x赋值,也不会说重新解析这个a.x为对象B的x
this绑定的硬绑定和软绑定实现
背景: 硬绑定可以把this强制绑定到指定的对象(`new`除外),防止函数调用应用默认绑定规则。但是会降低函数的灵活性,使用**硬绑定之后就无法使用隐式绑定或者显式绑定来修改this**。
var a = {b:1}; var b = {b:2} function func(){ console.log(this.b) } var bind1 = func.bind(a) bind1() //1 bind1.apply(b) //1 var copybind1 = bind1.bind(b) copybind1() //1
软绑定实现原理:给默认绑定指定一个全局对象和undefined以外的值**,那就可以实现和硬绑定相同的效果
// 默认绑定规则,优先级排最后 // 如果this绑定到全局对象或者undefined,那就把指定的默认对象obj绑定到this,否则不会修改this if(!Function.prototype.softBind) { Function.prototype.softBind = function(obj) { var fn = this;//对应要绑定的函数 // 捕获所有curried参数 var curried = [].slice.call( arguments, 1 ); //柯里化,预绑定参数 var bound = function() { return fn.apply( (!this || this === (window || global)) ? obj : this, curried.concat.apply( curried, arguments ) // ); }; bound.prototype = Object.create( fn.prototype );//继承原型,避免绑定后得到的函数原型丢失 return bound; }; }
function foo() { console.log("name:" + this.name); } var obj = { name: "obj" }, obj2 = { name: "obj2" }, obj3 = { name: "obj3" }; // 默认绑定,应用软绑定,软绑定把this绑定到默认对象obj var fooOBJ = foo.softBind( obj ); fooOBJ(); // name: obj // 隐式绑定规则 obj2.foo = foo.softBind( obj ); obj2.foo(); // name: obj2 <---- 看!!! // 显式绑定规则 fooOBJ.call( obj3 ); // name: obj3 <---- 看!!! // 绑定丢失,应用软绑定 setTimeout( obj2.foo, 10 ); // name: obj
this考题举例
var num = 1; var myObject = { num: 2, add: function() { this.num = 3; // 隐式绑定 修改 myObject.num = 3 // 严格模式下,报错。`TypeError: Cannot read property 'num' of undefined` // 即使非自执行函数,this也是window;这个不属于隐式绑定,容易误区 (function() { console.log(this.num); // 默认绑定 输出 1 this.num = 4; // 默认绑定 修改 window.num = 4 })(); console.log(this.num); // 隐式绑定 输出 3 }, sub: function() { console.log(this.num) // 因为丢失了隐式绑定的myObject,所以使用默认绑定 输出 4 } } myObject.add(); // 1 3 console.log(myObject.num); // 3 console.log(num); // 4 var sub = myObject.sub;// 丢失了隐式绑定的myObject sub(); // 4
var name = 'window' function Person (name) { this.name = name; this.show2 = () => console.log(this.name) } var personA = new Person('personA') personA.show2() //personA personA.show2.call(personB) // personA //另一种形式 var name = 'window' var person1 = { name: 'person1', show2: () => console.log(this.name) } var person2 = { name: 'person2' } person1.show2() //window person1.show2.call(person2) //window //结论 // 箭头函数的this无法通过bind,call,apply来**直接**修改(可以间接修改)
实现一个深拷贝
//没有考虑symbol和递归爆栈 function cloneDeep(source, hash = new WeakMap()) { if (typeof source !== 'object') return source; let target = Array.isArray(source) ? [] : {} //解决循环引用和引用丢失 if (hash.has(source)) { console.log('in') return hash.get(source) } hash.set(source, target) for (let item in source) { if (source.hasOwnProperty(item)) { if (typeof source[item] === 'object' && source[item] != null) { target[item] = cloneDeep(source[item], hash) } else { target[item] = source[item] } } } return target } //参考链接https://github.com/yygmind/blog/issues/29
node与浏览器的event loop 区别
https://juejin.im/post/5d5b4c2df265da03dd3d73e5 (浏览器的事件循环)
https://juejin.im/post/5c337ae06fb9a049bc4cd218#heading-12 (比较)
// node的运行环境 console.log('start') setTimeout(() => { console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, 0) setTimeout(() => { console.log('timer2') Promise.resolve().then(function() { console.log('promise2') }) }, 0) Promise.resolve().then(function() { console.log('promise3') }) console.log('end') //start=>end=>promise3=>timer1=>timer2=>promise1=>promise2 // 浏览器的运行环境 //start=>end=>promise3=>timer1=>promise1=>timer2=>promise2
一开始执行栈的同步任务(这属于宏任务)执行完毕后(依次打印出start end,并将2个timer依次放入timer队列),会先去执行微任务(这点跟浏览器端的一样),所以打印出promise3
然后进入timers阶段,执行timer1的回调函数,打印timer1,并将promise.then回调放入microtask队列,同样的步骤执行timer2,打印timer2;这点跟浏览器端相差比较大,timers阶段有几个setTimeout/setInterval都会依次执行,并不像浏览器端,每执行一个宏任务后就去执行一个微任务
如果是node11版本一旦执行一个阶段里的一个宏任务(setTimeout,setInterval和setImmediate)就立刻执行微任务队列,这就跟浏览器端运行一致
Node端,microtask 在事件循环的各个阶段之间执行
浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
对constructor的理解
export 和 export default
export 导出的变量实际上仍然可以受到原来模块的控制。不单单本身模块的a值会变化, 使得其他模块中引入的 a 值发生改变
export default 导出的是值,导出的就是普通变量 a 的值,以后 a 的变化与导出的值就无关了,修改变量 a,不会使得其他模块中引入的 default 值发生改变。