Open comTg opened 2 years ago
function myNew () {
let Constructor = Array.prototype.shift.call(arguments); // 1. 取出构造函数
let obj = {}; // 2. 执行会创建一个新对象
obj.__proto__ = Constructor.prototype;
var result = Constructor.apply(obj, arguments);
return typeof result === 'object' ? result : obj;
}
call方法的实现主要有以下三步,比如 fn.call(obj, a, b) :
Function.prototype.myCall = function (context) {
context = context ? Object(context) : window;
context.fn = this // 重置上下文
let args = [...arguments].slice(1)
let r = context.fn(...args) // 执行函数
delete context.fn // 删除属性
return r
}
与call大同小异,唯一差别就是apply传入的参数时数组格式
Function.prototype.myApply = function (context) {
context = context ? Object(context) : window
context.fn = this
let args = [...arguments][1]
if (!args) {
return context.fn()
}
let r = context.fn(...args);
delete context.fn
return r
}
bind方法和call, apply方法的差别是,他们都改变了上下文,但是bind没有立即执行函数
Function.prototype.myBind = function () {
let self = this,
context = [].shift.call(arguments),
args = [].slice.call(arguments);
return function () {
self.apply(context, [].concat(args, [].slice.call(arguments)));
}
}
空值合并操作符 ?? 类似于 || 运算符,当其左侧的操作数为 null 或者 undefined 时,返回右侧的操作数,否则返回左侧的操作数。
const value = value1 ?? value2;
// 等价于
const value = (value1 === undefined || value1 === null) ? value2 : value1;
const val1 = null ?? 'test' // test
const val2 = undefined ?? 'test' // test
从定义上看,空值合并操作符 ?? 类似于 || 运算符,但是是很不同的,看接下来的几个例子.
const val3 = 0 ?? 'test' // 0
const val4 = '' ?? 'test' // ''
const val5 = NaN ?? 'test' // NaN
const val6 = false ?? 'test' // false
|| 左侧的操作数会被强制转换成布尔值。因此,当使用 || 时,需要考虑左侧是否为 0, '', NaN, false 等值,如果为这几个值时,可能会出现非预期的结果。 注意,当 ?? 直接与 && 或 || 组合使用时,必须加上括号,否则会抛出 SyntaxError
null || undefined ?? "default"; // Uncaught SyntaxError: Unexpected token '??'
(null || undefined ) ?? "default"; // 返回 "default"
可选链操作符(?.)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效 可用于属性或方法
obj?.first
obj.test?.()
闭包有三步: 第一,外层函数嵌套内层函数; 第二, 内层函数使用外层函数的局部变量; 第三,把内层函数作为外层函数的返回值! 经过这样的三步就可以形成一个闭包! 闭包就可以在全局函数里面操作另一个作用域的局部变量! 当内部函数被保存到外部时,就会形成闭包; 闭包会导致原有作用域链不释放,造成内存泄露; 闭包作用:
闭包既能重复使用局部变量,又不污染全局! 实现公有变量,比如说函数累加器 做缓存 实现属性的私有化
增设执行回调等待时间来降低触发事件执行回调的频率
function debounce(callback, delay) {
let timer = null;
return function () {
let args = Array.prototype.slice.call(arguments)
let context = this
if (timer !== null) {
clearTimeout(timer)
}
timer = setTimeout(() => {
callback.apply(context, args)
}, delay)
}
}
function throttle (func, delay) {
let timer = null
return function () {
const context = this
const args = Array.prototype.slice(arguments)
if (!timer) {
timer = setTimeout(function () {
func.apply(context, args)
timer = null;
}, delay)
}
}
}
apply和call的区别
1. apply(): 传入两个参数,一个作为函数上下文的对象,另外一个是作为函数参数所组成的数组。
func.call(obj, 'C', 'D')
对于什么时候该用什么方法,其实不用纠结。如果你的参数本来就存在一个数组中,那自然就用 apply,如果参数比较散乱相互之间没什么关联,就用 call。
apply和call的用法
改变this的指向
借用别的对象的方法
调用函数 apply, call 方法都会使函数立即执行,因此它们可以用来调用函数
call和bind的区别
在 EcmaScript5 中扩展了叫 bind 的方法,在低版本的 IE 中不兼容。它和 call 很相似,接受的参数有两部分, 第一个参数是是作为函数上下文的对象,第二部分参数是个列表,可以接受多个参数。 它们之间的区别有以下两点
bind返回值是函数
参数的使用
低版本浏览器没有bind方法, 可以自己实现一个