javascript

执行上下文/作用域链/闭包

执行上下文

概念:执行上下文是评估和执行JavaScript代码的环境的抽象概念,每当JavaScript代码运行时都是在执行上下文中运行;
类型有3中:
1.全局执行上下文
2.函数执行上下文
3.eval函数执行上下文

作用域链

作用域链的作用是保证执行环境里有权访问的变量和函数是有限的,作用域链的变量只能向上访问,直到window对象为止,作用域链向下访问是不被允许的,作用域的作用是控制变量和函数的可见性和生命周期;

闭包

概念:闭包是指有权访问另一个函数作用域中变量的函数;
创建闭包的常见方式是在一个函数中建另一个函数,里面函数引用外层函数的变量;
闭包形成的原因:
函数在创建时,都会形成一个执行环境和作用域链,函数的作用域链包含指向外部函数的执行环境的指针,外部函数的作用域指向外部的外部,一直到全局指向函数;
闭包的优点:可以避免全局变量的污染,建立私有化的变量和方法;
闭包的缺点:(1)匿名函数的this指向全局window,解决方法:var that = this; (2)如果闭包的作用域链中包含HTML元素,导致无法减少元素引用的次数,垃圾回收机制不能回收(使用不当容易造成内存泄漏);

this/call/apply/bind

介绍JavaScript中的this

全局中this指向window;
非严格模式下函数中的this指向window;
严格模式下函数中的this指向undefined;
方法中this指向所有者对象;
事件中this指向接收事件的元素;
箭头函数没有自己的this,箭头函数的this指向定义时的this而不是运行时的this;

如何改变this指向

obj1.test.call(obj2,arg1,arg2)
obj1.test.apply(obj2,[arg1,arg2])

call和apply的区别

传递参数的不同:call接收一个一个参数,apply接收数组

如何实现call如何实现apply

实现call:
Function.prototype.myCall = function(context){
context.fn = this; //(this表示function test(){})
var args = […arguments].slice(1)
return context.fn(…args)
}
obj1.test.myCall(obj2,arg1,arg2)
实现apply
Function.prototype.myApply = function(context){
context.fn = this
var result = context.fn(…arguments[1])
delete context.fn
return result
}

如何实现bind

//手动实现bind:
Function.prototype.myBind = function(context){
if(typeof this !== ‘function’){
throw new TypeError(‘Error’)
}
//context相当于obj2(上下文)
var _this = this//function(age,age2){}
var args = […arguments].slice(1)//[123,456]
return function F(){
if(this instanceof F){//因为在外面调用函数F(),此处的this相当于window
return new _this(…args,…arguments)
}
return _this.apply(context,args.concat(…arguments))
}
}

原型/继承

JavaScript中的原型

__proto__和constructor是对象独有的;
(contructor表示指向该对象的构造函数)
prototype属性是函数所独有的,但由于JS中函数也是对象的一种,所以函数也有__proto_和constructor属性;
__proto__和prototype关系举例:
eg.
function Foo(){};
var f = new Foo();
f.proto == Foo.prototype;//true
f.constructor == Foo;//true

原型链是什么

每个对象在其内部都会初始化一个__proto__属性,当访问对象的某个属性时,如果对象上找不到这个属性,就会去对象的__proto__上去找这个属性,如果对象的__proto__也找不到这个属性,就去对象的__proto__的__proto__,这样一层一层向上找,直到Object,在Object没有找到返回null,形成一条__proto__连起来的链条。
在这里插入图片描述

如何利用原型实现继承

实现原型继承的方法:让原型对象等于另一个对象的实例
原型链继承流程图

promise

promise是什么

promise是异步编程的一种解决方案。
有三种状态fulfilled,rejected,pending,状态的改变有两种:1.pending–>fulfilled
2.pending–>rejected
状态一旦改变便不再变,promise构造函数接收两个参(resolve,reject)
new Promise()–>执行异步操作?成功了-》执行resolve()->Promise对象(resolved状态)->回调onResolved()(then里面的)–>
失败了-》执行reject() --》Promise对象Rejected状态------>回调onRejected()—>返回promise
为什么要用Promise?(promise指定回调函数的方式更加灵活,具体如下)
旧的:因为纯回调函数执行异步操作之前就要指定回调函数,顺序为先指定回调函数,再启动异步任务(如下图);
优点一:Promise:启动异步任务–》返回Promise对象–》给Promise绑定回调函数(一旦得到promise说明启动了异步操作)
优点二:解决回调地狱问题(回到地狱是指回调函数嵌套使用,外部回调函数的异步执行结果 作为 内部回调函数的条件,不便于阅读,不便于异常的处理(每一层都需要调用异常处理的回调))
解决方案:promise的链式调用
终极解决:async/await

如何实现promise

//异步链式调用的Promise
    function MyPromise(executor){
        var self = this
        self.onResolvedCallback = []
        function resolve(value){
            setTimeout(()=>{
                self.data = value
                self.onResolvedCallback.forEach(callback => callback(value))
            })
        }
        executor(resolve.bind(self))
    }
    MyPromise.prototype.then = function(onResolved){
        var self = this
        return new MyPromise(resolve => {
            self.onResolvedCallback.push(function(){
                var result = onResolved(self.data)
                if(result instanceof MyPromise){
                    result.then(resolve)
                }else{
                    resolve(result)
                }
            })
        })
    }
 //上述代码链接:https://juejin.im/post/5e6f4579f265da576429a907#comment
//例子测试:
        new MyPromise((resolve)=>{
        setTimeout(()=>{
            console.log('立即执行器1')
            resolve(2)
        },1000)
    }).then(res=>{
        console.log(`resolve传递过来的值:${res}`)
        return new MyPromise((resolve)=>{
            setTimeout(()=>{
                resolve(3)
            },1000)
        })
    }).then(res2=>{
        console.log(res2)
    })

async/await

1.概念:async是什么?
async就是Generator函数的语法糖。
1.1 async对于Generator的改进在于:
(1)内置执行器(async带有内置执行器,而generator函数必须依靠执行器-Co模块)
(2)更好的语义
(3)更广泛的适用性(Co模块规定yield后面必须是Thunk函数或者Promise对象,而await后面可以是Promise,也可以是原始类型的值,但这时等于同步操作)
(4)返回值是Promise,可以用then方法指定下一步操作(generator返回值是Iterator对象),进一步说,async可以看做是多个异步操作包装成的Promise对象,await命令就是内部then命令的语法糖。
2.使用时需要注意
await后面的异步操作出错等同于async返回的promise被reject,防止出错的方法是将await放到try catch中。
3.实现原理:async的实现原理就是将Generator函数和自动执行器包装在一个函数里。

深浅拷贝

介绍深拷贝和浅拷贝

1.浅拷贝概念(只进行一层级的拷贝):创建一个新对象,这个对象有着原始对象属性的精准拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引用类型,拷贝的是引用类型的内存地址,如果其中某一个对象改变了这个值,就会影响到另一个对象。
2.深拷贝概念(无限层级的拷贝):将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响到原来对象。

如何实现浅拷贝

浅拷贝的实现:
1.首先是JavaScript中自带的:数组的slice方法,数组的concat方法。
举例子:
2.for循环的实现:

//1.浅复制
var obj1 = {
	name: 'Hu',
	age: 26,
	friends: ['ss',['aa','ff'],'xx']
}
function shallowCopy(src){
var dst = {};
	for(var prop in src){
		if(src.hasOwnProperty(prop)){
			dst[prop] = src[prop];
		}
	}
	return dst;
}
var obj3 = shallowCopy(obj1);
obj3.name = 'yy';
console.log(obj3);//{name: "yy", age: 26, friends: Array(3)}
console.log(obj1);//{name: "Hu", age: 26, friends: Array(3)}

//2.注意 直接赋值操作与浅复制不同:
var obj2 = obj1;
obj2.name = 'yy1';
console.log(obj1);//{name: "yy1", age: 26, friends: Array(3)}
console.log(obj2);//{name: "yy1", age: 26, friends: Array(3)}
//直接赋值会同时改变原始对象,因为二者引用指向的是同一个对象;

如何实现深拷贝

1.JavaScript中自带的:JSON序列化:
JSON.parse(JSON.stringfy(obj1));
JSON序列化实现深拷贝的问题:在遇到undefined 、Symbol、该对象不能正常的序列化。
2.

            //深拷贝
            function deepCopy(m){
                var target = {}
                for(var i in m){
                    if(m.hasOwnProperty(i)){
                        if(typeof m[i] == 'object'){
                            target[i] = deepCopy(m[i])
                        }else{
                            target[i] = m[i]                    
                        }
                    }
                }
                return target

            }
            var a2 = deepCopy(a11)
            console.log(a2.b.c === a11.b.c)

实现深拷贝需要注意

Object.assign(target,source)只深拷贝了原始对象的第一层级,后面的层级都是浅拷贝;
eg:

var obj11 = {};
Object.assign(obj11,obj1);
obj11.name = 'hust';
obj11.age = 25;
obj11.friends[2] = [1,2];
console.log(obj11);//{name: "hust", age: 25, friends: Array(3)}
//对于对象中的引用类型Object.assign实现的是浅拷贝,原始对象中的数组也随之改变了;
//0: "ss"
//1: (2) ["aa", "ff"]
//2: (2) [1, 2]
console.log(obj1);//{name: "Hu", age: 26, friends: Array(3)}
//0: "ss"
//1: (2) ["aa", "ff"]
//2: (2) [1, 2]

解决循环引用的问题

事件机制Event Loop

参考掘金: https://juejin.im/post/5c3d8956e51d4511dc72c200
Event Loop事件循环,指浏览器或者Node一种解决单线程运行不会阻塞的机制,也就是我们经常使用的异步原理。
在JavaScript中任务被分为两种:宏任务和微任务。

如何实现一个事件的发布订阅

事件循环

事件轮询:监听调用栈并确定调用栈是否为为空,如果为空就检查消息队列是否有挂起的回调函数等待执行。
在这里插入图片描述

宏任务和微任务的区别

宏任务:每次执行栈中执行的代码可以理解为一个宏任务。
宏任务有哪些:script整体代码,setTimeout,setInterval, setImmediate(浏览器暂不支持,只有IE10支持),I/O,UIRendering
点击事件属于宏任务:点击事件属于事件触发线程,用来控制事件循环(如点击事件,ajax)(所以点击事件是宏任务);

微任务:当前宏任务执行完之后立即执行的任务。
微任务有哪些:process.nextTick() promise,MutationObserver(用来监视DOM变动,不会马上出发等到所有的DOM操作结束后才触发),ObjectObserver(废弃)

nextTick()执行顺序:优先于其他微任务先先执行,process.nextTick() 将callback添加到回调队列,如果存在nextTick队列就会清空队列中所有的回调函数,并优先于其他的micro先执行;

函数式编程

Web Worker

二级目录

二级目录

常用方法

ES6

	--未更新完
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值