共计 4975 个字符,预计需要花费 13 分钟才能阅读完成。
块级作用域(执行后果题)
块级作用域
阮一峰 块级作用域
var a = 1999;
{console.log(a); // function a(){}
a = 2020;
function a() {}
a = 2021;
}
console.log(a); // 2020
我的了解:(原理可能谬误,然而便于了解,谬误点在于块级不能申明函数,浏览器会有本人的反对行为,相似于函数表达式申明——参考阮一峰。申明也不要用函数申明的形式,用函数表达式的形式。)
在块级中,编译过程,函数申明变量晋升,执行过程无影响。因而是 function a(){}
在块级内部,编译过程,内部有 a,无影响。执行过程时,开始查找,因为 a 的查找程序是从词法环境({})到变量环境(var),查找到最近的,因而是 2020(留神此处是执行阶段,function a(){} 的变量晋升是编译阶段)
ps. 函数和变量相比,会被优先晋升。(我的了解:这意味着函数会替换掉变量晋升)
事件冒泡、事件捕捉、事件代理(事件委托)
事件冒泡:略
事件捕捉:略
事件代理:利用事件冒泡,将事件绑定在父元素中
target.addEventListener(type, listener, useCapture);
,其中useCapture
为 false 时,为事件冒泡,默认是 false。
Object
常见的办法:
Object.defineProperty
定义的 description: {value, writable, configurable, emunable},或者 {set, get, configurable, emunable}Object.create
、Object.keys
、Object.values
、Object.entries
、Object.toString
Object.preventExtensions
组织扩大对象
作用域、作用域链和上下文
作用域是指在函数定义时,申明变量的空间。
作用域链是指在变量查找过程中,从以后上下文查找,逐层往父级,直至全局。
函数申明时,会有个 scope
属性,蕴含所有父级的变量。此时 VO 对象,蕴含外部函数、变量、形参,存储在上下文中。
函数执行时,AO 对象,蕴含外部函数、变量、形参、外部 this,挂载到作用域链上,
作用域、作用域链与执行上下文栈入门理解
原形链
待补充
Promise 与异步
常见的异步申请:
原生
var request = new HttpXMLRequest()
request.open('GET', url);
request.responseType = 'json'
request.onload = function(){}
request.send()
实现 Promise
待补充
// Promise
// Promise.prototype.then
// Promise.prototype.all
// Promise.prototype.resolve
// Promise.prototype.race
事件循环与事件队列
事件循环
组成:
- 事件队列(单位是音讯,音讯关联着回调函数,从音讯队列中弹出后会调用回调函数,造成执行帧)
- 执行栈(单位是帧,蕴含函数中的变量与参数)
- 堆(保留对象构造)
同步工作与异步工作
window.requestAnimationFrame() 既不是宏工作也不是微工作,而是在浏览器下次重绘的时候执行
闭包
两个次要的特点:
- 通过函数阻止内部函数对外部变量的援用
- 函数能够应用内部的变量
参考
闭包中的 this
因为闭包是执行在内存中,所以 this 通常指向全局,能够通过 call 扭转。
闭包的作用
- 通过立刻执行函数,模仿块级作用域,缩小向全局作用域申明变量,另外因为立刻执行函数在执行完后内部没有援用,那么内存会立刻开释
- 应用 var 申明时,利用立刻执行函数遍历时 i 能精确获取
- 实现面向对象编程(不是通过 new 结构)
function Person(){
var name = 'default';
return {getName : function(){return name;},
setName : function(newName){name = newName;}
}
};
var p1 = Person();
console.log(p1.getName()); // default
p1.setName('wang');
console.log(p1.getName()); // wang
闭包的问题
- 在闭包中援用 dom 会导致循环援用,无奈 GC(援用计数)
// IE9 之前甚至无奈销毁 dom
function closure(){var element = document.getElementById(ID);
element.onclick = function(){console.log(element.id);
}
// 销毁 element
// element = null
}
- this 指向
- 闭包返回部分作用域变量时,内存不会立刻开释
宏工作 和 微工作
常见异步考题
es5/es6/es7/es8
待补充
class 中的 super
super 既能够当函数也能够当对象应用。
当函数时,相当于是父类的构造函数,只能用在子类的构造函数中,this 指向子类实例。
class A {constructor() {this.show();
}
show(){console.log('A 实例');
}
}
class B extends A {constructor() {super();
}
show(){console.log('B 实例');
}
}
new B() // B 实例
当对象时,在个别函数应用时,super 相当于父类原型对象,this 指向子类实例。
class A {constructor() {this.x = 'A';}
say() {console.log(this.x)
}
}
class B extends A {constructor() {super();
this.x = 'B'
}
}
let b = new B();
console.log(b.say()) // B
ps. 留神该揭示
class A {constructor() { // 在构造函数上定义的属性和办法相当于定义在父类实例上的,而不是原型对象上
this.p = 2;
}
}
class B extends A {get m() {return super.p;}
}
let b = new B();
console.log(b.m) // undefined
// 引申题
function A(x) {this.p = x}
A.prototype.p = 2
// 此时的 p 通过构造函数曾经申明
new A().p // undefined
super 在静态方法 this 指向父类
常见的代码
防抖与节流
防抖是指,在一段时间内累计后触发,例如输入框输出文字时,监听 onChange。
function debounce(fn, delay) {
let timer
return function () {
const self = this;
const args = arguments;
if (timer) {clearTimeout(timer);
}
timer = setTimeout(function(){fn.apply(self, args);
}, delay)
}
}
let log = debounce(function(){console.log('!')}, 5000)
window.addEventListener('resize',log)
节流是指,在一段时间内多次重复触发仅执行一次,例如反复点击。
function throttle(fn, delay) {
let timer
return function () {
const self = this;
const args = arguments;
if (timer) {return;}
timer = setTimeout(function() {self.apply(fn, args)
timer = null;
}, delay)
}
}
let log = throttle(function(){console.log('!')}, 3000)
window.addEventListener('click',log)
造成这种区别的起因:
节流当第一次执行是 arg 就固定了,也就是说如果用节流放到输入框 onChange 场景时,值将是第一个输出的数字。
防抖,通过一直的 clearTimeout,来更新要执行的函数,直到不触发后,期待 delay 后执行,delay 的作用是在此期间如果再次触发,则会再次 clearTimeout
手写 new
// Object.create 会更新 __proto__,也就是 [[Prototype]],维持原形链
function create (proto) {if (typeof proto !== 'object' && typeof proto !== 'function') {throw new TypeError("原型只能是对象")
}
if (proto === null) {throw new TypeError("不能为空")
}
// function F() {}
//F.prototype = proto;
// return new F();
proto.constructor.prototype = proto
return new proto.constructor()}
function newOperator(ctor) {if (typeof ctor !== 'function') {throw '构造函数必须是办法'}
newOperator.target = ctor;
// es6 能够间接应用 Object.create
// const instance = Object.create(ctor.prototype)
const instance = create(ctor.prototype)
const args = [].slice.call(arguments, 1)
// 绑定 this,并执行构造函数
const r = ctor.apply(instance, args);
// 如果构造函数有返回,则返回构造函数
if (r) {return r;}
// 实例
return instance;
}
function Person (name) {this.name = name}
const w = newOperator(Person, "zs")
console.log(w)
手写 bind
function bind(fn, obj) {const args = [].slice.call(arguments, 1);
const selef = this;
return function bound() {return fn.call(obj, [].slice.call(arguments, 1).concat(args))
}
}
const h = {name: 'zs',}
function say() {console.log(this.name)
}
const bound = bind(say, h)
bound()
Object.is Polyfill
if (!Object.is) {
Object.defineProperty(Object, "is", {value: function (x, y) {if (x === y) {
// 1. 如果 x === y,且均不为 0 时
// 2. 如果 x,y 均为 0,判断符号是否相等
return x !== 0 || 1 / x === 1 / y;
} else {
// NaN 与本人比拟。蕴含:Number.NaN, 0/0, NaN
return x != x && y != y;
}
}
})
}
如何实现 Array.reduce()
待补充
curry 与 compose
待补充
Object.assign()
待补充
实现字符串 repeat
// 原生 repeat 'ni'.repeat(3);
// 'ninini'
// 实现一
String.prototype.repeatString1 = function (n) {return Array(n + 1).join(this);
}
console.log('ni'.repeatString1(3));
// 实现二
String.prototype.repeatString2 = function (n) {return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));
js 模板引擎
Function('let a = 1');
其余
其余