知识体系
核心知识点
作用域链
上下文content
作用域链
-
演示面试题
// 面试题 let a = 'global'; function course() { let b = 'zhawa' session() function session() { let c = 'session'; teacher() // console.log('d', d); function teacher() { // let d = 'yy'; // 块作用域 console.log('d-yy', d); //变量提升-提升范围:当前作用域 var d = 'yy'; console.log('d', d); //作用域生效 console.log('b', b); console.log('a', a); //作用域向上查找 } } } course();
-
提升优先级的问题
console.log(xxx); var xxx = 'xxxx' function xxx(){ console.log('xxxx-1') }
结论:
变量先被提升,提升到前面,函数再被提升,提升到变量后面,打印后面覆盖前面
-
函数是天然的隔离方案—模块化
console.log(t) //仍可以访问t if(true) { // 没形成作用域 var t = 33; // 要形成,用let,const形成块级作用域 }
作用域链结论:
1.对于作用域链,我们可以直接通过创建态去定位链条中的某一环 ----上下文
2.手动取消链条环或者作用域的时候,可以通过块级作用域做性能优化----性能优化
上下文content
this是在执行时动态读取上下文所决定的
-
考察点: 各使用态中的指针指向
-
函数直接调用-this指向window
function foo () { console.log('函数内部的this:', this); } foo();
-
隐式绑定
// 隐式绑定 function fn() { console.log('隐式绑定', this) } var obj = { a: 1, fn } obj.fn = fn obj.fn() //输出this指向obj
// 面试题1 const foo = { bar: 10, fn: function() { console.log(this.bar) //undefined console.log(this) //window } } let fn1 = foo.fn //取出 fn1()
// 面试题2 const o1 = { text: 'o1', fn: function() { // 直接使用上下文 - 传统分活 // console.log('o1-this', this) return this.text } } const o2 = { text : 'o2', fn: function() { // 求助领导-部门协作 return o1.fn() } } const o3 = { text: 'o3', fn: function() { // 直接借人 let fn = o1.fn return fn() } } console.log('o1', o1.fn()) //o1 console.log('o2', o2.fn()) //o1 console.log('o3', o3.fn()) //undefined
结论:
1.在执行函数的时候,执行时调用的上一方—上下文
2.公共函数 | 全局调用指向window
-
-
显示绑定(bind | apply | call)
-
知识点补充
-
bind
-
绑定函数与目标函数
const obj = { x: 24, //目标函数 getX: function(){ return this.x; } } //绑定函数 const _getX = obj.getX; console.log(_getX()); const _newGetX = obj.getX.bind(obj); console.log(_newGetX()); // 传参 const boundFun = fn.bind(thisArg, arg1, arg2) const boundFun = (...resetParmas) => fn.call(thisArg, arg1, arg2, ...resetParmas);
-
多重绑定
// 多重绑定-this值、参数顺序 // 严格模式,防止this被封装到包装对象中 "use strict"; function log(...args) { console.log(this, ...args); } const boundLog = log.bind('new value', 1, 2); const newBoundLog = boundLog.bind('new new value', 3, 4,5); newBoundLog(7, 8); // new value 1 2 3 4 5 7 8
-
绑定函数构造
// 绑定函数构造---绑定函数传的this无效,new关键字构造绑定函数,this指向为目标函数 class Base { constructor(...args) { console.log(new.target === Base); console.log(args); } } const BoundBase = Base.bind(null, 1, 2,3, 4); new BoundBase(); /* true [ 1, 2, 3, 4 ] */ // 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this,如 Reflect.construct 的参数所示)。如果直接构造绑定函数,new.target 将指向目标函数(也就是说,绑定函数对 new.target 是透明的)。 class Base{} const boundBase = Base.bind(null, 1,2); console.log(new Base instanceof boundBase); console.log(boundBase.length); console.log(boundBase.name); /* 绑定函数还会继承目标函数的原型链。然而,它不会继承目标函数的其他自有属性(例如,如果目标函数是一个类,则不会继承其静态属性)。 */
-
将方法转化为实用函数
/* 以 Array.prototype.slice() 为例,你可以使用它将类数组对象转换为真正的数组。 */ const slice = Array.prototype.slice; slice.call(arguments); /* 请注意,你不能保存 slice.call 并将其作为普通函数调用,因为 call() 方法还会读取其应该调用的函数作为其 this 值。在这种情况下,你可以使用 bind() 来绑定 call() 的 this 值。 */ const unboundSlice = Array.prototype.slice; const slice = Function.prototype.call.bind(unboundSlice); slice(arguments);
-
-
apply
-
传参与参数展开
//调用 const numbers = [5, 6, 2, 3,7]; const max = Math.max.apply(null, numbers); const min = Math.min.apply(null, numbers); console.log('max, min', max, min); //参数展开 /* 一般而言,fn.apply(null, args) 等同于使用参数展开语法的 fn(...args),只是在前者的情况下,args 期望是类数组对象,而在后者的情况下,args 期望是可迭代对象。 */ const array = ["a", "b"]; const elements = [0, 1,2]; array.push.apply(array, elements); console.info(array); array.push(...elements); console.log(array); //批量添加数组元素 const array = ["a", "b"]; const elements = [0, 1,2]; array.push.apply(array, elements); console.info(array); array.push(...elements); console.log(array);
-
与call的区别
与 call() 几乎完全相同,只是函数参数在 call() 中逐个作为列表传递,而在 apply() 中它们会组合在一个对象中,通常是一个数组——例如,func.call(this, "eat", "bananas") 与 func.apply(this, ["eat", "bananas"])。
-
使用 apply() 和内置函数
/* 使用 apply() 和内置函数 */ const numbers = [5, 6, 2, 3,7]; let max = Math.max.apply(null, numbers); let min = Math.min.apply(null, numbers); console.log('max, min:', max, min); // 与基于简单循环的算法相比 max = -Infinity; min = +Infinity; for(let i=0; i<numbers.length; i++) { if(numbers[i] > max) { max = numbers[i]; } if(numbers[i] < min) { min = numbers[i]; } } console.log('max, min:', max, min); /* 但要注意:通过使用 apply()(或展开语法)来处理任意长的参数列表,你可能会超过 JavaScript 引擎的参数长度限制。 如果你的值数组可能会增长到数万个,可以使用混合策略:将数组的片段分批通过 apply 调用函数 */ function minOfArray (arr) { let min = +Infinity; const QUANTUM = 32768; for(let i=0; i<arr.length; i+= QUANTUM) { const submin = Math.min.apply(null, arr.slice(i, Math.min(i+QUANTUM, arr.length))); min = Math.min(submin, min); } return min; } const min = minOfArray([5, 6, 2, 4, 7]); console.log(min);
-
-
call
使用同apply,只有参数形式不同。
-
-
apply\bind\call方法区别
call <=> apply, 传参不同, 依次传入,数组(类数组)传入
bind返回值不同,返回函数
-
面试: 手写apply和bind
/* 手写apply和bind */ // 1.需求===>手写bind--->bind的位置 ---> Function.prototype ==>原型 Function.prototype.newBind = function() { // 2.bind原理--改变this的指向 const _this = this; const args = Array.prototype.slice.call(arguments); const newThis = args.shift(); // 返回值不执行 ===> 返回函数 return funciton () { // 执行核心 return _this.newApply(newThis, args); } } // 2.内层实现 Function.prototype.newApply = function(context) { // 参数兜底 context = context || window; // 临时挂载执行环境 context._this = this; let result = arguments[1] ? context.fn(arguments[1]) : context.fn(); delete context._this; return result; }
-
闭包:突破作用域
//简单演示闭包的含义 function mail() { let content = '信'; // content作用域为mail函数内,突破此作用域,全局可访问如下操作 return function() { return content; } } mail()();
-