1. 作用域/作用域链
- 作用域(Scope)
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。内层作用域可以访问外层作用域的变量,反之则不行。全局作用域,函数作用域,块级作用域(let/const 声明)
- 作用域链
当前作用域找不到的变量,逐渐向父级作用域(创建函数的作用域)寻找,形成的关系链
- 作用域与执行上下文
JavaScript 的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样。执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值。
2. 事件委托
- 事件委托
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
- 事件流
一个完整的事件流是从window开始,最后回到window的一个过程
事件流被分为3个阶段:1-5捕获阶段,5-6:目标阶段,6-10:冒泡阶段
3. this
this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的
情况1:非严格模式下,如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
- 改变this
(1)new构造函数
如果返回值是一个对象(非null),那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例
// 手写new
var a = new _new(myFunction,"Li","Cherry");
funtion _new(myFunction,...args){
var obj = {};
obj.__proto__ = myFunction.prototype;
var result = myFunction.apply(obj, args);
return typeof result === 'obj'? result : obj;
}
(2)箭头函数
箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined
(3)在函数内部使用_this = this
(4)使用apply,call,bind
fun.apply(thisArg, [argsArray])
fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg:在 fun 函数运行时指定的 this 值。需要注意的是,指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
4. 闭包
可以从内部函数访问外部函数作用域
5. 原型与原型链,继承
Person函数拥有prototype属性,Person.prototype.constructor属性(指向函数本身),var p1 = new Person(),p1.proto = Person.prototype,p1拥有Person函数实例以及可以访问Person函数的原型prototype属性。
p1.name = 'p1',在p1修改属性实际上是在p1的自身对象中添加属性name,会遮蔽原型上的同名属性name。
- hasOwnProperty() 方法
在原型中的属性和new返回的实例对象不存在的属性都会返回fasle。
- in 操作符
new返回的实例对象和原型中有一个地方存在这个属性,就返回true。
var o = {a: 1};
// o 这个对象继承了 Object.prototype 上面的所有属性
// o 自身没有名为 hasOwnProperty 的属性
// hasOwnProperty 是 Object.prototype 的属性
// 因此 o 继承了 Object.prototype 的 hasOwnProperty
// Object.prototype 的原型为 null
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// (Array.prototype 中包含 indexOf, forEach 等方法)
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于 Function.prototype
// (Function.prototype 中包含 call, bind等方法)
// 原型链如下:
// f ---> Function.prototype ---> Object.prototype ---> null
继承方式
new初始化
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = new foo;
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Object.create()
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
foo.prototype
);
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
Object.setPropertyOf()
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = {
bar_prop: "bar val"
};
Object.setPrototypeOf(
proto, foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
proto
function foo(){}
foo.prototype = {
foo_prop: "foo val"
};
function bar(){}
var proto = {
bar_prop: "bar val",
__proto__: foo.prototype
};
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
6. 浅拷贝和深拷贝
- 浅拷贝
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
function shallowClone(source) {
var target = {};
for(var i in source) {
if (source.hasOwnProperty(i)) {
target[i] = source[i];
}
}
return target;
}
- 深拷贝
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
// 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
7. 防抖节流
- 防抖(debounce)
指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内又触发了该事件,则会重新开始算规定时间。
const debounce = (fun, timeout = 1000) => {
let flag = null
return (...args) => {
if (flag) {
clearTimeout(flag)
}
flag = setTimeout(function (){
flag = null
fun.apply(this, args)
}, timeout);
}
}
- 节流(throttle)
当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内又触发了该事件,则什么也不做,也不会重置定时器.
const throttle = (fun, timeout = 1000) => {
let flag = false
return (...args) => {
if (!flag) {
flag = true
setTimeout(function (){
flag = false
fun.apply(this, args)
}, timeout);
}
}
}