【前端面试】五、JavaScript 6-10
目录
6. this指向
6.1 this的绑定
- 默认绑定:全局环境中,this默认绑定到window
- 隐式绑定:this隐式绑定到函数调用时的直接对象。隐式丢失:被隐式绑定的函数丢失绑定对象
- 显式绑定:通过call()、apply()、bind()方法把对象绑定到this上
- new绑定:new构造函数调用的this绑定
6.2 改变this指向
call、apply、bind、外部变量闭包、箭头函数(没有自己的this,指向所定义时的上下文)
6.3 call()、apply()、bind()
apply、call:都能改变this指向并立即执行函数fn,但传入的参数形式不同
bind:改变this指向并返回一个新的函数,这个函数不会马上执行
- fn.apply(作用域对象,[参数1,参数2])
- fn.call(作用域对象,参数1,参数2)
- fn.bind(作用域对象,参数1,参数2)
// 手写call方法
Function.prototype.mycall = function(obj) {
if(typeof this !== "function"){
throw new Error("error");
}
obj = obj || window; // 设置对象obj
obj.fn = this; // 给对象添加方法fn, this 指向fn
let arg = [...arguments].splice(1); // 取参数a, b
let result = obj.fn(...arg); // 执行对象的方法fn
return result;
}
fn.mycall(obj, a, b)
// 手写apply方法
Function.prototype.apply = function (context, argsArr) {
if(typeof this !== "function"){
throw new Error("error");
}
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
context[fnSymbol](...argsArr);
delete context[fnSymbol];
}
// bind
Function.prototype.bind = function (context, ...args) {
if(typeof this !== "function"){
throw new Error("error");
}
context = context || window;
const fnSymbol = Symbol("fn");
context[fnSymbol] = this;
return function (..._args) {
args = args.concat(_args);
context[fnSymbol](...args);
delete context[fnSymbol];
}
}
Function.prototype.bind2 = function(context){
var self = this;
var args = Array.prototype.slice.call(arguments,1);
var fNOP = function(){};
var fbound = function(){
self.apply(this instanceof self ? this:context,
args.concat(Array.prototype.slice.call(arguments)));
}
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
}
7. 箭头函数
- 箭头函数中的this指向所定义时上下文中的this(沿作用域链向上查找),而不是调用时所在的对象;
let str = 'global';
let obj = {
str: 'private',
getStr: function(){
console.log(this.str); // this指向obj
}
}
obj.getStr(); // private
let str = 'global';
let obj = {
str: 'private',
getStr: () => {
console.log(this.str); // this指向window
}
}
obj.getStr(); // global
- 不可以用作构造函数,不能使用new命令,否则报错,没有new.target值;
const Person = (name) => {
this.name = name
}
let tom = new Person('Tom') // Person is not a constructor
- 没有arguments对象,可用...rest参数代替;
const fn = () => {
console.log(arguments);// arguments is not defined
}
fn(1, 2, 3)
- 没有原型对象prototype
function fun() {}
fun.prototype
const fn = () => {}
fn.prototype
8. 防抖和节流
防抖:多次触发事件后,事件处理函数在n秒内只执行一次,并且是在触发操作结束时执行,若在n秒内再次触发则重新计算。用于下拉触底加载下一页、即时查询、scroll事件场景。
解决原理:对处理函数进行延时操作,若设定的延时到来之前再次触发事件,则清除上一次的延时操作定时器,重新定时。
// 等待wait后执行事件
function debounce(fn, wait) {
let timer = null;
return function() {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments)
}, wait)
}
}
// 立即执行事件,等待wait后再次执行事件
function debounce(fn, wait) {
let timer = null;
return function() {
if(timer) {
clearTimeout(timer);
}
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if(callNow) {
fn.apply(this, arguments)
}
}
}
// scroll事件
let timer;
window.onscroll = function () {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
//滚动条位置
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
timer = null;
},200)
}
节流:一定时间只调用一次 ,连续发生的事件在n秒内只执行一次,场景:提交表单,高频监听
function throttle(fn, delay) {
let timer = null;
return function() {
const context = this,
args = arguments
if(!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null
}, delay);
}
}
}
function throttle1(fn, delay) {
let curTime = Date.now();
return function () {
const context = this,
args = arguments,
nowTime = Date.now();
if (nowTime - curTime >= delay) {
curTime = Date.now();
return fn.apply(context, args);
}
};
}
function throttle2(fn, ms) {
let flag = false;
return (...args) => {
if (flag) {
return;
}
flag = true;
setTimeout(() => {
fn(...args);
flag = false;
}, ms);
};
}
参考文献:【前端性能】高性能滚动 scroll 及页面渲染优化 - ChokCoco - 博客园
9. promise
9.1 promiseA+规范:Promises/A+ (promisesaplus.com)
- Promise对象为异步编程提供统一接口,每个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数
- 一个promise可能有三种状态:等待(pending)、已完成(resolved/fulfilled)、已拒绝(rejected);对象的状态不受外部影响,promise只能从pending转到fulfilled或者rejected,一旦状态改变就不会再变
- 实例化promise对象需要传入函数,该函数包含resolve、reject两个函数作为参数,resolve和reject中可以传入参数在then回调函数中使用
- promise必须提供一个then方法,then接收两个函数作为参数(可选参数);第一个参数是成功时的回调,在promise由"pending"转换到"resolved"时调用;另一个是失败时的回调,在promise由"pending"转换到"rejected"时调用;如果then接收的参数不是函数则发生透传
- then方法必须返回一个promise对象,同一个promise的then可以链式调用,回调的执行顺序跟它们被定义时的顺序一致
- catch相当于.then(null,reject),当then中没有传入reject时,错误会冒泡进入catch中,若传入了reject则被reject捕获不会进入catch
- then中的回调函数中发生的错误只会在下一级的then中被捕获,不会影响该promise的状态
- promise能够在then中的回调函数return出promise对象或其他值,也可以throw出错误对象,但如果没有return,将默认返回undefined,后面的then中的回调参数接收到的将是undefined
9.2 promise方法
静态方法:Promise.all() Promise.any() Promise.allSettled() Promise.race()
实例方法:Promise.prototype.then() Promise.prototype.catch() Promise.prototype.finally()
9.3 手写promise:
参考文献:实现一个完美符合Promise/A+规范的Promise
// Promise的基本用法
let promise1 = new Promise((resolve,reject) =>
setTimeout(() => resolve('ok'), 1000))
promise1.then((val) => console.log(val))
// 串行执行三个promise,A、B和C
A.then(B).then(C).catch(...)
(async () => {await a(); await b(); await c();})()
// Promise手写
function myPromise(excutor) {
// 1.执行结构
let self = this;
self.status = 'pending';
self.value = null;
self.reson = null;
// 6.添加回调函数缓存数组
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
// 4.判断状态,做相应处理
function resolve(value) {
self.value = value;
self.status = 'fulFilled'
// 8.状态改变,执行回调函数
self.onFulfilledCallbacks.forEach(item => item(value))
}
function reject(reson) {
self.reson = reson;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(item => item(reason))
}
// 3. 执行一遍
try {
excutor(resolve, reject)
}catch(err) {
reject(err)
}
}
// 2.添加then
myPromise.prototype.then = function(onFulfilled, onRejected) {
// 5. 判断then中的参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (data) => resolve(data);
onRejected = typeof onRejected === 'function' ? onRejected : (err) => {throw err};
let self = this;
// 7.添加回调函数到缓存数组解决异步
if(self.status === 'pending') {
self.onFulfilledCallbacks.push(onFulfilled);
self.onRejectedCallbacks.push(onRejected);
}
}
// 9.添加catch
myPromise.prototype.catch = function(fn) {
return this.then(null, fn)
}
9.4 ajax封装promise
var myNewAjax = function(url){
return new Promise((resolve,reject) =>{
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.status == 200 && xhr.readyState == 4{
var json = JSON.parse(xhr.responseText);
resolve(json)
}else if(xhr.readyState == 4 && xhr.status != 200){
reject('error');
}
}
})
}
10. async
async表示这是一个async异步函数,await只能用在这个函数里面
await表示等待异步操作的返回结果,再继续执行,await后一般是一个promise对象
如果async函数返回的是一个同步的值,将等同于return Promise.resolve(value)
(async () => {await new promise();})()
let timer = async function timer(){
return new Promise((resolve,reject)=>{
setTimeout(() => resolve('500'),500)
})
}
timer()
.then(result=> console.log(result)) // 500
.catch(err => console.log(err.message));
//返回一个同步的值
let sayHi = async function sayHi(){
let hi = await 'helloworld';
return hi;//等同于return Promise.resolve(hi);
}
sayHi().then(result=>console.log(result));