前端強化(六月)

深刻this

1.this

箭頭函數的綁定沒法被修改(new也不行)。前端

/**
 * 非嚴格模式
 */

var name = 'window'

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1()
personA.show1.call(personB)

personA.show2()
personA.show2.call(personB)

personA.show3()()
personA.show3().call(personB)
personA.show3.call(personB)()

personA.show4()()
personA.show4().call(personB)
personA.show4.call(personB)()

正確答案以下:

personA.show1() // personA,隱式綁定,調用者是 personA
personA.show1.call(personB) // personB,顯式綁定,調用者是 personB

personA.show2() // personA,首先personA是new綁定,產生了新的構造函數做用域,
				// 而後是箭頭函數綁定,this指向外層做用域,即personA函數做用域
personA.show2.call(personB) // personA,同上

personA.show3()() // window,默認綁定,***調用者是window***
personA.show3().call(personB) // personB,顯式綁定,調用者是personB
personA.show3.call(personB)() // window,默認綁定,***調用者是window***

personA.show4()() // personA,箭頭函數綁定,this指向外層做用域,即personA函數做用域
personA.show4().call(personB) // personA,箭頭函數綁定,call並無改變外層做用域,
							  // this指向外層做用域,即personA函數做用域
personA.show4.call(personB)() // personB,解析同題目1,最後是箭頭函數綁定,
							  // this指向外層做用域,即改變後的person2函數做用域

複製代碼

2. call

foo.call(obj)(window) foo裏面的this仍是第一個面試

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

var bar = function() {
    foo.call( obj );
};

bar(); // 2
setTimeout( bar, 100 ); // 2

// 硬綁定的bar不可能再修改它的this
bar.call( window ); // 2
複製代碼

3. 隱式綁定

function foo(el) {
	console.log( el, this.id );
}

var obj = {
    id: "awesome"
}

var myArray = [1, 2, 3]
// 調用foo(..)時把this綁定到obj
myArray.forEach( foo, obj );
// 1 awesome 2 awesome 3 awesome
複製代碼

4. 手寫一個new實現

function Person() {...}
// 使用內置函數new
var person = new Person(...)

// 使用手寫的new,即create
var person = create(Person, ...)

function create() {
	// 建立一個空的對象
    var obj = new Object(),
	// 得到構造函數,arguments中去除第一個參數
    Con = [].shift.call(arguments);
	// 連接到原型,obj 能夠訪問到構造函數原型中的屬性
    obj.__proto__ = Con.prototype;
	// 綁定 this 實現繼承,obj 能夠訪問到構造函數中的屬性
    var ret = Con.apply(obj, arguments);
	// 優先返回構造函數返回的對象
	return ret instanceof Object ? ret : obj;
};
複製代碼

5. ES6 call 和 apply 的模擬實現

1 call 
Function.prototype.call = function (context) {
  context = context ? Object(context) : window; 
  context.fn = this;

  let args = [...arguments].slice(1);
  let result = context.fn(...args);

  delete context.fn
  return result;
}
2 apply
Function.prototype.apply = function (context, arr) {
    context = context ? Object(context) : window; 
    context.fn = this;
  
    let result;
    if (!arr) {
        result = context.fn();
    } else {
        result = context.fn(...arr);
    }
      
    delete context.fn
    return result;
}
複製代碼

6. 進階序列問題:用 JS 實現一個無限累加的函數 add

add(1); // 1 add(1)(2); // 3 add(1)(2)(3); // 6 add(1)(2)(3)(4); // 10 // 以此類推promise

function add(a) {
	function sum(b) { // 使用閉包
    	a = a + b; // 累加
    	return sum;
 	}
 	sum.toString = function() { // 重寫toString()方法
        return a;
    }
 	return sum; // 返回一個函數
}

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 
咱們知道打印函數時會自動調用 toString()方法,函數 add(a) 返回一個閉包 sum(b),函數 sum() 中累加計算 a = a + b,只須要重寫sum.toString()方法返回變量 a 就OK了。
複製代碼

7. ES6 中如何獲取URL地址中的參數

function query(url){
  var paramsArr=url.split('?')[1].split('&')//[c=3,d=4]
  let ret={};
  paramsArr.forEach((item)=>{
    let key=item.split('=')[0];
    let val=item.split('=')[1];
    ret[key]=val;
  })
  return ret
}
 var result = query('https://shiting.com/s?c=3&d=4')
 console.log(result); //{ c: '3', d: '4' }
複製代碼

8. js面試題 a爲什麼值時 輸出爲1

var a; //?
if(a==1&&a==2&&a==3){
  console.log(1);
}
// 剖析 只要a是對象就行 對象每進行一次比較或者拼接都會執行toString()方法
var a={
  i:1,
  toString:function(){
    return this.i++
  }
}
if (a == 1 && a == 2 && a == 3) {
  console.log(1);
}

var num=1;
var b = {
  toString: function () {
    return ++num
  }
}
console.log(b+'');//2 string
console.log(b+'');//3 string
console.log(b+1);//5 number
console.log(b==5);//true

複製代碼

9. bind 原生

// f.bind(obj1,2,3)(2)驗證
Function.prototype.bindq=function(){
  let self = this;
  let con=[...arguments].shift();
  let args = [...arguments];
  return function(){
    return self.apply(con, args.concat([...arguments]))
  }
}


Function.prototype.bind = function () {
  let self = this,
    args = Array.from(arguments),
    context = args.shift();
    console.log(args,context);
    
  return function () {
    return self.apply(context, args.concat(...arguments))
  }
}
// 實現bind() new 
複製代碼

10. 手寫promise

// 手寫一個promise 面試夠用

function myPromise(constructor){
  let self=this;
  self.status='pending';
  self.value=undefined;
  self.reason=undefined;
  function resolve(value) {
    if (self.status === 'pending'){
      self.status = 'resolved';
      self.value = value;
    }
  };
  function reject(value) {
    if (self.status === 'pending'){
      self.status = 'rejected';
      self.reason = value;
    }
  };
  try{
    // 調用構造函數
    constructor(resolve, reject)
  }catch(e){
    reject(e);
  }
}
myPromise.prototype.then = function (onFullfilled, onRejected) {
  
  switch (this.status) {
    case 'resolved':
      onFullfilled(this.value)
      break;
    case 'rejected':
      onRejected(this.reason)
      break;
  
    default:
      break;
  }
}

var p=new myPromise(function(resolve,reject){resolve(9)});
p.then(function (x) {
  console.log(x);//9
})

複製代碼

10. 手寫防抖(Debouncing)和節流(Throttling)

scroll 事件自己會觸發頁面的從新渲染,同時 scroll 事件的 handler 又會被高頻度的觸發, 所以事件的 handler 內部不該該有複雜操做,例如 DOM 操做就不該該放在事件處理中。 針對此類高頻度觸發事件問題(例如頁面 scroll ,屏幕 resize,監聽用戶輸入等),有兩種經常使用的解決方法,防抖和節流。bash

防抖閉包

當一次事件發生後,事件處理器要等必定閾值的時間,若是這段時間過去後 再也沒有 事件發生,就處理最後一次發生的事件。假設還差 0.01 秒就到達指定時間,這時又來了一個事件,那麼以前的等待做廢,須要從新再等待指定時間。最後一次觸發事件app

奮鬥

// 防抖動函數
function debounce(fn,wait=50,immediate) {
    let timer;
    return function() {
        if(immediate) {
            fn.apply(this,arguments)
        }
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=> {
            fn.apply(this,arguments)
        },wait)
    }
}

                <!--驗證-->
// 簡單的防抖動函數
// 實際想綁定在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}

// 採用了防抖動
window.addEventListener('scroll',debounce(realFunc,500));
// 沒采用防抖動
window.addEventListener('scroll',realFunc);


複製代碼

節流函數

固定頻率調用,間隔相同ui

奮鬥

//時間間隔
function throttle(fn,wait=50){
  // 節流
  let prev=new Date();
  return function(){
    let now=new Date();
    if(now-prev > wait){
      fn.apply(this,arguments);
      prev = new Date();
    }
  }
}
function throttle1(fn,wait=50){
  // 定時器
  let canRun=true;
  return function(){
    if (!canRun) return;
    canRun = false;
    setTimeout(() => {
       fn.apply(this, arguments);
       canRun = true;
    }, wait);
  }
}
// 簡單的防節流函數
// 實際想綁定在 scroll 事件上的 handler
function realFunc() {
  console.log("Success");
}
// 採用了防節流
window.addEventListener('scroll', throttle(realFunc, 500));
// 沒采用防節流
window.addEventListener('scroll', realFunc);


複製代碼

參考前端勸退師this

相關文章
相關標籤/搜索