JavaScript有一些重要的概念需要更加透徹的理解,大概講解以下幾個:
1.call,apply ,bind的使用,什麼是argument ?bind的原生js實現
call和apply都是爲函數綁定執行的上下文,指定一個對象來替換函數執行時候的this,它倆的區別在於call需要傳入完整的參數列表,而apply可以傳入一個參數數組。bing函數則是爲函數綁定執行的上下文,需要傳入一個this作爲參數,不同於call和apply,bind不是立即執行的。下面看一下bind的實現:
//bind函數就是返回一個函數,但是函數的this綁定到指定的this上
Function.prototype.myBind = function(newThis){
if(typeof this !== 'function') {
throw new TypeError('need bing to a function')
}
let self = this;
let arg = [].slice.call(arguments,1)
return function() {
self.apply(newThis,arg.concat([].slice.call(arguments)))
}
}
argument是函數內部的參數,每個都有,可以用argument.length來訪問參數的長度,但是argument不是真的數組,需要用bind裏面的數組的slice方法轉爲真正的數組,我們可以衝argument的length實現類似函數重載的效果
2.this的顯式綁定和隱式綁定還有new綁定
this在函數執行的時候,會隱式綁定到包含這個函數的對象上,this指向這個對象。顯示綁定就是上面說的call apply bind,再就是new出來的函數的實例,this自動指向這個實例對象。
那什麼是隱式綁定丟失呢?
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
這種情況下,bar在調用的時候沒有在對象上,就默認綁定到window,嚴格模式下和node環境中都是undefined。再看一個例子
var obj = {
id: 'vexekefo',
cool() {
console.log(this.id);
}
};
var id = 'someone';
obj.cool(); // vexekefo
setTimeout(obj.cool, 100); // someone
解決辦法,我們可以使用箭頭函數,箭頭函數沒有自己的this,它的this是定義它的上下文決定的
3.JavaScript的事件循環機制,宏任務和微任務,JavaScript的單線程如何理解
Js的事件循環機制:js引擎遇到一個異步事件後並不會一直等待其返回結果,而是會將這個事件掛起,繼續執行執行棧中的其他任務。當一個異步事件返回結果後,js會將這個事件加入與當前執行棧不同的另一個隊列,我們稱之爲事件隊列。被放入事件隊列不會立刻執行其回調,而是等待當前執行棧中的所有任務都執行完畢, 主線程處於閒置狀態時,主線程會去查找事件隊列是否有任務。如果有,那麼主線程會從中取出排在第一位的事件,並把這個事件對應的回調放入執行棧中,然後執行其中的同步代碼...,如此反覆,這樣就形成了一個無限的循環。這就是這個過程被稱爲“事件循環(Event Loop)”的原因。
分爲宏任務隊列,和微任務隊列,宏任務是setTimeOut和setInterval,微任務是promise,node環境下還有process.nextTick,
每次從宏任務取一個,再清空微任務隊列,然後進入下一輪循環
4.JavaScript的原型鏈,作用域分別是什麼,可以用來做什麼
js中的一切皆對象,每個對象都有一個prototype屬性,它指向對象的原型對象,而原型對象也有prototype屬性,繼續指向原型對象的原型對象,最終都是Object。Object的prototype是null,我們可以通過_proto_在瀏覽器訪問
我們可以用它做繼承,在原型鏈上定義的函數和屬性,順着原型鏈往下的對象都可以訪問到
作用域鏈:在作用域最前端的是活動對象,而最後端是全局執行環境window(瀏覽器宿主中);變量訪問原則是,根據作用域前端往上進行搜索,如果提前搜索到變量,則停止搜索,例如上面這個例子中,name變量的值是"sub"因爲其在最前端的變量對象中已經定義了,就不會往上繼續檢索;
5.JavaScript的繼承方式主要有什麼,如何實現
組合繼承,原型繼承,寄生組合繼承,在原型鏈上定義可複用的函數,在構造函數內定義自己的變量。
6.JavaScript在瀏覽器中的事件,捕獲和冒泡
瀏覽器事件分爲三個階段,先捕獲,找到發生事件的DOM,然後處於事件發生階段,然後冒泡,我們可以利用冒泡實現事件的委託,最常用的就是渲染了很多li標籤,不必給每個li綁定onclick事件,而是在他們的父元素ul上綁定一個,如果不想讓子組件的某個事件冒泡觸發父組件的同名事件,可以使用stopPropagation方法
7.JavaScript的基本數據類型有哪些,基本包裝類型有哪些,它們在堆棧中的存儲方式是什麼
基本數據類型有string number null undefined bool symbol,還有個複雜數據類型object,基本包裝類型有Number String Date Object Regexp等,基本數據類型存儲在棧中,定義後不可更改,而對象類型都是在棧中存儲數據的引用地址,真實的數據存在堆中
8.實現對象的深拷貝和淺拷貝,它們各自應用的場景
淺拷貝是隻拷貝了對象的引用地址,在堆中沒有新開一塊複製原有的數據,深拷貝則是會在堆中新開一塊空間,完全複製一份,
深拷貝可以用轉化成JSON的方式,但是隻適用於Number Sring Boolean Array,就是這種可以被JSON表示的對象,其他複雜的類型則需要單獨編寫函數來處理
9 Javascript的閉包是什麼,什麼時候需要使用閉包
js的閉包:函數體內定義的函數可以拿到函數的局部變量,可以用這個封裝私有屬性,可以用來實現模塊化,單例模式,
閉包的作用是可以用閉包訪問局部變量,可以讓局部變量駐留在內存中
10JavaScript模塊化,不同的標準有哪些,模塊化是如何實現的(聯繫閉包)
我們開發React的時候用的exports import是ES6的語法,開發node的時候的module.export和require是commonjs的標準。
const Helmet = require('react-helmet').default
const ReactDomServer = require('react-dom/server')
const ejs = require('ejs')
const serialize = require('serialize-javascript')
const SheetsRegistry = require('react-jss').SheetsRegistry
const colors = require('material-ui/colors')
const createMuiTheme = require('material-ui/styles').createMuiTheme
const create = require('jss').create
const preset = require('jss-preset-default').default
const asyncBootstrapper = require('react-async-bootstrapper').default
可以看到兩者的不同,react-helmet用的ES6語法編寫的模塊,如果要在node使用,我們需要用default去拿到真正的模塊實例
11 new 操作符具體做了什麼
new操作符新建一個空對象,然後把構造函數的原型對象賦給新對象,然後把構造函數賦值給原型對象的constructor屬性,然後
執行構造函數,把新對象傳進去,參數也傳進去
let New = function (P) {
let o = {};
let arg = Array.prototype.slice.call(arguments,1);
o.__proto__ = P.prototype;
P.prototype.constructor = P;
P.apply(o,arg);
return o;
12async是什麼,如何使用
是ES7的異步,配合await一起使用
13箭頭函數的指向
指向定義箭頭函數時候的詞語作用域
14事件委託機制
就是冒泡的時候子元素的事件委託給父元素執行
15.0.1+0.2 !== 0.3原因和解決辦法
js中浮點數表達不精確,可以用tofix(2)+parseInt
16數組的去重,扁平化
去重:可以用new Set,直接構造
扁平化:使用reduce函數+Array.isArray+遞歸
17函數柯里化與惰性求值
函數柯里化我們在中間件學習的時候看過store =>next=>action => {}
這種方式可以累積參數,在需要調用的時候再調用函數,不是立即執行
18定時器爲何存在不準的情況,手寫一個倒計時
js定時器不準,前端重要邏輯要用後端傳來的時間
19 let const var的區別
let const沒有變量提升 var有變量提升 let const可以綁定到塊級作用域
20 class的實現機制
class是基於ES5的function實現的,是原型繼承的語法糖
21 防抖和節流函數怎麼寫
都是避免避免無限觸發綁定在dom上的事件,一些比如scroll,輸入框輸入等觸發頻率較高的,我們不能讓綁定的回調一直觸發,因此有了防抖和節流兩種辦法。
防抖適用於輸入搜索,我們不要每次input的lvalue變化我們都去搜索一次,我們在輸入延遲個500ms左右再執行一次。就是一次輸入後,如果500ms沒有再輸入,我們就執行搜索匹配
節流適用於scroll拉取網絡數據,我們每過一段時間拉取一下,也不能說最後一次拉取,那中途就沒有用來渲染的數據了
<html>
<head></head>
<body>
<div style='height:400px;width:800px;line-height:400px;background-color: gray;text-align: center;font-size: 30px;'>
</div>
<script>
let num = 1;
ele = document.querySelector('div');
function count() {
ele.innerHTML = num;
num++;
}
ele.onmousemove = throttle(count,1000)
// 添加防抖,在最後一次觸發後再過1000ms執行,最後一次之前都不執行
function debounce(fn,wait) {
let timeout;
return function() {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn,wait);
}
}
//添加節流,減少執行次數,比如下滑拉取數據,不適合最後一次拉取,適合中途過一段時間拉取一次
function throttle(fn,wait) {
let preTime = Date.now();
return function() {
let now = Date.now();
if(now - preTime >= wait) {
fn();
preTime = Date.now()
}
}
}
</script>
</body>
</html>