箭頭函數的綁定沒法被修改(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函數做用域
複製代碼
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
複製代碼
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
複製代碼
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;
};
複製代碼
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;
}
複製代碼
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了。
複製代碼
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' }
複製代碼
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
複製代碼
// 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
複製代碼
// 手寫一個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
})
複製代碼
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