1.undefined 和 null 有什么區(qū)別?undefined和null之間的差異之前,我們先來看看它們的相似類。let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint'];Boolean(value)或!!value將其轉(zhuǎn)換為布爾值時,值為false。
undefined是未指定特定值的變量的默認值,或者沒有顯式返回值的函數(shù),如:console.log(1),還包括對象中不存在的屬性,這些 JS 引擎都會為其分配 undefined 值。let _thisIsUndefined;const doNothing = () => {};const someObj = { a : 'ay', b : 'bee', c : 'si'};console.log(_thisIsUndefined); // undefinedconsole.log(doNothing()); // undefinedconsole.log(someObj['d']); // undefinednull是“不代表任何值的值”。 null是已明確定義給變量的值。在此示例中,當fs.readFile方法未引發(fā)錯誤時,我們將獲得null值。
null和undefined時,我們使用==時得到true,使用===時得到false: console.log(null == undefined); // true console.log(null === undefined); // false2. && 運算符能做什么&& 也可以叫邏輯與,在其操作數(shù)中找到第一個虛值表達式并返回它,如果沒有找到任何虛值表達式,則返回最后一個真值表達式。它采用短路來防止不必要的工作。
if語句const router: Router = Router();router.get('/endpoint', (req: Request, res: Response) => { let conMobile: PoolConnection; try { //do some db operations } catch (e) { if (conMobile) { conMobile.release(); } }});&&操作符
3. || 運算符能做什么||也叫或邏輯或,在其操作數(shù)中找到第一個真值表達式并返回它。這也使用了短路來防止不必要的工作。在支持 ES6 默認函數(shù)參數(shù)之前,它用于初始化函數(shù)中的默認參數(shù)值。console.log(null || 1 || undefined); // 1function logName(name) { var n = name || 'Mark'; console.log(n);}logName(); // 'Mark'4. 使用 + 或一元加運算符是將字符串轉(zhuǎn)換為數(shù)字的最快方法嗎?+是將字符串轉(zhuǎn)換為數(shù)字的最快方法,因為如果值已經(jīng)是數(shù)字,它不會執(zhí)行任何操作。5. DOM 是什么?
document對象表示DOM。它為我們提供了許多方法,我們可以使用這些方法來選擇元素來更新元素內(nèi)容,等等。6. 什么是事件傳播?window為止;而在“捕獲階段”中,事件從window開始向下觸發(fā)元素 事件或event.target。
7. 什么是事件冒泡?window為止。<div class='grandparent'> <div class='parent'> <div class='child'>1</div> </div></div>
addEventListener方法具有第三個可選參數(shù)useCapture,其默認值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上記錄child,parent,grandparent,html,document和window,這就是事件冒泡。8. 什么是事件捕獲?window開始,一直到觸發(fā)事件的元素。<div class='grandparent'> <div class='parent'> <div class='child'>1</div> </div></div>
addEventListener方法具有第三個可選參數(shù)useCapture,其默認值為false,事件將在冒泡階段中發(fā)生,如果為true,則事件將在捕獲階段中發(fā)生。如果單擊child元素,它將分別在控制臺上打印window,document,html,grandparent和parent,這就是事件捕獲。9. event.preventDefault() 和 event.stopPropagation()方法之間有什么區(qū)別?event.preventDefault() 方法可防止元素的默認行為。如果在表單元素中使用,它將阻止其提交。如果在錨元素中使用,它將阻止其導(dǎo)航。如果在上下文菜單中使用,它將阻止其顯示或顯示。 event.stopPropagation()方法用于阻止捕獲和冒泡階段中當前事件的進一步傳播。10. 如何知道是否在元素中使用了 |
| x | y | x==y |
true。條件1,因為x和y具有相同的類型和值。條件4,在比較之前將y轉(zhuǎn)換為數(shù)字。條件2。條件7,因為y是boolean類型。條件8。使用toString()方法將數(shù)組轉(zhuǎn)換為字符串,該方法返回1,2。條件8。使用toString()方法將對象轉(zhuǎn)換為字符串,該方法返回[object Object]。| x | y | x ===y |
===運算符,則第一個示例以外的所有比較將返回false,因為它們的類型不同,而第一個示例將返回true,因為兩者的類型和值相同。let a = { a: 1 };let b = { a: 1 };let c = a;console.log(a === b); // 打印 false,即使它們有相同的屬性console.log(a === c); // true
console.log語句返回false,而第二個console.log語句返回true。a和c有相同的引用地址,而a和b沒有。!!運算符可以將右側(cè)的值強制轉(zhuǎn)換為布爾值,這也是將值轉(zhuǎn)換為布爾值的一種簡單方法。console.log(!!null); // falseconsole.log(!!undefined); // falseconsole.log(!!''); // falseconsole.log(!!0); // falseconsole.log(!!NaN); // falseconsole.log(!!' '); // trueconsole.log(!!{}); // trueconsole.log(!![]); // trueconsole.log(!!1); // trueconsole.log(!![].length); // false逗號運算符在一行中計算多個表達式。它從左到右求值,并返回右邊最后一個項目或最后一個操作數(shù)的值。let x = 5;x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);function addFive(num) {return num + 5;}
x的值為27。首先,我們將x的值增加到6,然后調(diào)用函數(shù)addFive(6)并將6作為參數(shù)傳遞并將結(jié)果重新分配給x,此時x的值為11。之后,將x的當前值乘以2并將其分配給x,x的更新值為22。然后,將x的當前值減去5并將結(jié)果分配給x x更新后的值為17。最后,我們將x的值增加10,然后將更新的值分配給x,最終x的值為27。編譯和執(zhí)行。var關(guān)鍵字進行聲明),還會為它們提供默認值: undefined。var聲明的變量,或者函數(shù)聲明才會被提升,相反,函數(shù)表達式或箭頭函數(shù),let和const聲明的變量,這些都不會被提升。console.log(y);y = 1;console.log(y);console.log(greet('Mark'));
function greet(name){ return 'Hello ' + name + '!';}
var y;undefined,1, Hello Mark!。function greet(name) {return 'Hello ' + name + '!';}var y; // 默認值 undefined// 等待“編譯”階段完成,然后開始“執(zhí)行”階段/*console.log(y);y = 1;console.log(y);console.log(greet('Mark'));*/
function greet(name) { return 'Hello ' + name + '!';}
var y;
//start 'execution' phase
console.log(y);y = 1;console.log(y);console.log(greet('Mark'));全局作用域——在全局命名空間中聲明的變量或函數(shù)位于全局作用域中,因此在代碼中的任何地方都可以訪問它們。
//global namespacevar g = 'global';function globalFunc(){function innerFunc(){console.log(g);// can access 'g' because 'g' is a global variable}innerFunc();}
函數(shù)作用域——在函數(shù)中聲明的變量、函數(shù)和參數(shù)可以在函數(shù)內(nèi)部訪問,但不能在函數(shù)外部訪問。
function myFavoriteFunc(a) { if (true) { var b = 'Hello ' + a; } return b;}
myFavoriteFunc('World');
console.log(a); // Throws a ReferenceError 'a' is not definedconsole.log(b); // does not continue here塊作用域-在塊{}中聲明的變量(let,const)只能在其中訪問。
function testBlock(){if(true){let z = 5;}return z;}testBlock(); // Throws a ReferenceError 'z' is not defined
/* 作用域鏈
內(nèi)部作用域->外部作用域-> 全局作用域 */
// 全局作用域 var variable1 = 'Comrades'; var variable2 = 'Sayonara';
function outer(){ // 外部作用域 var variable1 = 'World'; function inner(){ // 內(nèi)部作用域 var variable2 = 'Hello'; console.log(variable2 + ' ' + variable1); } inner(); } outer(); // Hello World// 全局作用域var globalVar = 'abc';function a(){console.log(globalVar);}a(); // 'abc'
a函數(shù)時,全局作用域是a閉包的一部分。globalVar在圖中沒有值的原因是該變量的值可以根據(jù)調(diào)用函數(shù)a的位置和時間而改變。但是在上面的示例中,globalVar變量的值為abc。var globalVar = 'global';var outerVar = 'outer'
function outerFunc(outerParam) { function innerFunc(innerParam) { console.log(globalVar, outerParam, innerParam); } return innerFunc;}
const x = outerFunc(outerVar);outerVar = 'outer-2';globalVar = 'guess'x('inner');guess outer inner。outerFunc函數(shù)并將返回值innerFunc函數(shù)分配給變量x時,即使我們?yōu)?/span>outerVar變量分配了新值outer-2,outerParam也繼續(xù)保留outer值,因為重新分配是在調(diào)用outerFunc之后發(fā)生的,并且當我們調(diào)用outerFunc函數(shù)時,它會在作用域鏈中查找outerVar的值,此時的outerVar的值將為 'outer'。innerFunc的x變量時,innerParam將具有一個inner值,因為這是我們在調(diào)用中傳遞的值,而globalVar變量值為guess,因為在調(diào)用x變量之前,我們將一個新值分配給globalVar。const arrFuncs = [];for(var i = 0; i < 5; i++){arrFuncs.push(function (){return i;});}console.log(i); // i is 5for (let i = 0; i < arrFuncs.length; i++) {console.log(arrFuncs[i]()); // 都打印 5}
var關(guān)鍵字創(chuàng)建一個全局變量,當我們 push 一個函數(shù)時,這里返回的全局變量i。因此,當我們在循環(huán)后在該數(shù)組中調(diào)用其中一個函數(shù)時,它會打印5,因為我們得到i的當前值為5,我們可以訪問它,因為它是全局變量。let 來代替 var 的聲明。const falsyValues = ['', 0, null, undefined, NaN, false];false 的值。Boolean 函數(shù)或者 !! 運算符。'use strict' 是 ES5 特性,它使我們的代碼在函數(shù)或整個腳本中處于嚴格模式。嚴格模式幫助我們在代碼的早期避免 bug,并為其添加限制。變量必須聲明后再使用
函數(shù)的參數(shù)不能有同名屬性,否則報錯
不能使用with語句
不能對只讀屬性賦值,否則報錯
不能使用前綴 0 表示八進制數(shù),否則報錯
不能刪除不可刪除的屬性,否則報錯
不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
eval不能在它的外層作用域引入變量
eval和arguments不能被重新賦值
arguments不會自動反映函數(shù)參數(shù)的變化
不能使用arguments.callee
不能使用arguments.caller
禁止this指向全局對象
不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
增加了保留字(比如protected、static和interface)
消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
消除代碼運行的一些不安全之處,保證代碼運行的安全;
提高編譯器效率,增加運行速度;
為未來新版本的Javascript做好鋪墊。
this 值是什么?this指的是當前正在執(zhí)行或調(diào)用該函數(shù)的對象的值。this值的變化取決于我們使用它的上下文和我們在哪里使用它。const carDetails = {name: 'Ford Mustang',yearBought: 2005,getName(){return this.name;},isRegistered: true};console.log(carDetails.getName()); // Ford Mustang
getName方法中我們返回this.name,在此上下文中,this指向的是carDetails對象,該對象當前是執(zhí)行函數(shù)的“所有者”對象。var name = 'Ford Ranger';var getCarName = carDetails.getName;
console.log(getCarName()); // Ford RangerFord Ranger,這很奇怪,因為在第一個console.log語句中打印的是Ford Mustang。這樣做的原因是getCarName方法有一個不同的“所有者”對象,即window對象。在全局作用域中使用var關(guān)鍵字聲明變量會在window對象中附加與變量名稱相同的屬性。請記住,當沒有使用“use strict”時,在全局作用域中this指的是window對象。console.log(getCarName === window.getCarName); // trueconsole.log(getCarName === this.getCarName); // true
this和window引用同一個對象。apply和call方法。console.log(getCarName.apply(carDetails)); // Ford Mustangconsole.log(getCarName.call(carDetails)); // Ford Mustangapply和call方法期望第一個參數(shù)是一個對象,該對象是函數(shù)內(nèi)部this的值。IIFE或立即執(zhí)行的函數(shù)表達式,在全局作用域內(nèi)聲明的函數(shù),對象內(nèi)部方法中的匿名函數(shù)和內(nèi)部函數(shù)的this具有默認值,該值指向window對象。(function (){console.log(this);})(); // 打印 'window' 對象function iHateThis(){console.log(this);}iHateThis(); // 打印 'window' 對象const myFavoriteObj = {guessThis(){function getName(){console.log(this.name);}getName();},name: 'Marko Polo',thisIsAnnoying(callback){callback();}};myFavoriteObj.guessThis(); // 打印 'window' 對象myFavoriteObj.thisIsAnnoying(function (){console.log(this); // 打印 'window' 對象});
myFavoriteObj對象中的name屬性(即Marko Polo)的值,則有兩種方法可以解決此問題。this 值保存在變量中。const myFavoriteObj = { guessThis(){ const self = this; // 把 this 值保存在 self 變量中 function getName(){ console.log(self.name); } getName(); }, name: 'Marko Polo', thisIsAnnoying(callback){ callback(); }};const myFavoriteObj = {guessThis(){const getName = () => {//copies the value of 'this' outside of this arrow functionconsole.log(this.name);}getName();},name: 'Marko Polo',thisIsAnnoying(callback){callback();}};
this。它復(fù)制了這個封閉的詞法作用域中this值,在這個例子中,this值在getName內(nèi)部函數(shù)之外,也就是myFavoriteObj對象。const o = {};console.log(o.toString()); // logs [object Object]o對象中不存在o.toString方法,它也不會引發(fā)錯誤,而是返回字符串[object Object]。當對象中不存在屬性時,它將查看其原型,如果仍然不存在,則將其查找到原型的原型,依此類推,直到在原型鏈中找到具有相同屬性的屬性為止。原型鏈的末尾是Object.prototype。console.log(o.toString === Object.prototype.toString); // logs true// which means we we're looking up the Prototype Chain and it reached// the Object.prototype and used the 'toString' method.
function (){}包裹在在括號()內(nèi),然后再用另一個括號()調(diào)用它,如:(function(){})()(function(){ ...} ());
(function () { ...})();
(function named(params) { ...})();
(() => {
});
(function (global) { ...})(window);
const utility = (function () { return { ... }})IIFE的結(jié)果保存到變量中,以便稍后使用。<script src='https:///somelibrary.js'></script>
omelibr.js的鏈接,它提供了一些我們在代碼中使用的全局函數(shù),但是這個庫有兩個方法我們沒有使用:createGraph和drawGraph,因為這些方法都有bug。我們想實現(xiàn)自己的createGraph和drawGraph方法。<script src='https:///somelibrary.js'></script><script> function createGraph() { // createGraph logic here } function drawGraph() { // drawGraph logic here }</script><script src='https:///somelibrary.js'></script><script>function myCreateGraph() {// createGraph logic here}function myDrawGraph() {// drawGraph logic here}</script>
<script src='https:///somelibrary.js'></script><script> const graphUtility = (function () { function createGraph() { // createGraph logic here } function drawGraph() { // drawGraph logic here } return { createGraph, drawGraph } })</script>graphUtility 變量,用來保存IIFE執(zhí)行的結(jié)果,該函數(shù)返回一個包含兩個方法createGraph和drawGraph的對象。var li = document.querySelectorAll('.list-group > li');for (var i = 0, len = li.length; i < len; i++) {li[i].addEventListener('click', function (e) {console.log(i);})
list-group類的ul元素,它有5個li子元素。當我們單擊單個li元素時,打印對應(yīng)的下標值。但在此外上述代碼不起作用,這里每次點擊 li 打印 i 的值都是5,這是由于閉包的原因。var關(guān)鍵字聲明變量時,就創(chuàng)建全局變量i。因此,當我們單擊li元素時,它將打印5,因為這是稍后在回調(diào)函數(shù)中引用它時i的值。var li = document.querySelectorAll('.list-group > li');for (var i = 0, len = li.length; i < len; i++) { (function (currentIndex) { li[currentIndex].addEventListener('click', function (e) { console.log(currentIndex); }) })(i);}i的值并將其傳遞給currentIndex參數(shù),因此調(diào)用IIFE時,每次迭代的currentIndex值都是不同的。apply() 方法調(diào)用一個具有給定this值的函數(shù),以及作為一個數(shù)組(或類似數(shù)組對象)提供的參數(shù)。const details = {message: 'Hello World!'};function getMessage(){return this.message;}getMessage.apply(details); // 'Hello World!'
call()方法的作用和 apply() 方法類似,區(qū)別就是call()方法接受的是參數(shù)列表,而apply()方法接受的是一個參數(shù)數(shù)組。const person = { name: 'Marko Polo'};
function greeting(greetingMessage) { return `${greetingMessage} ${this.name}`;}
greeting.apply(person, ['Hello']); // 'Hello Marko Polo!'Function.prototype.call 方法的用途是什么?call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數(shù)來調(diào)用一個函數(shù)。const details = {message: 'Hello World!'};function getMessage(){return this.message;}getMessage.call(details); // 'Hello World!'
apply() 方法類似,只有一個區(qū)別,就是 call() 方法接受的是一個參數(shù)列表,而 apply() 方法接受的是一個包含多個參數(shù)的數(shù)組。const person = { name: 'Marko Polo'};
function greeting(greetingMessage) { return `${greetingMessage} ${this.name}`;}
greeting.call(person, 'Hello'); // 'Hello Marko Polo!'apply()方法可以在使用一個指定的 this 值和一個參數(shù)數(shù)組(或類數(shù)組對象)的前提下調(diào)用某個函數(shù)或方法。call()方法類似于apply(),不同之處僅僅是call()接受的參數(shù)是參數(shù)列表。const obj1 = {result:0};const obj2 = {result:0};function reduceAdd(){let result = 0;for(let i = 0, len = arguments.length; i < len; i++){result += arguments[i];}this.result = result;}reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // 15reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15
bind() 方法創(chuàng)建一個新的函數(shù),在 bind() 被調(diào)用時,這個新函數(shù)的 this 被指定為 bind() 的第一個參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時使用。import React from 'react';
class MyComponent extends React.Component { constructor(props){ super(props); this.state = { value : '' } this.handleChange = this.handleChange.bind(this); // 將 “handleChange” 方法綁定到 “MyComponent” 組件 }
handleChange(e){ //do something amazing here }
render(){ return ( <> <input type={this.props.type} value={this.state.value} onChange={this.handleChange} /> </> ) }}function higherOrderFunction(param,callback){return callback(param);}
(var func = function(){})、傳參(function func(x,callback){callback();})、返回(function(){return function(){}}),這樣的函數(shù)也稱之為第一級函數(shù)(First-class Function)。不僅如此,JavaScript中的函數(shù)還充當了類的構(gòu)造函數(shù)的作用,同時又是一個Function類的實例(instance)。這樣的多重身份讓JavaScript的函數(shù)變得非常重要。map() 方法創(chuàng)建一個新數(shù)組,其結(jié)果是該數(shù)組中的每個元素都調(diào)用一個提供的函數(shù)后返回的結(jié)果。function map(arr, mapCallback) { // 首先,檢查傳遞的參數(shù)是否正確。 if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') { return []; } else { let result = []; // 每次調(diào)用此函數(shù)時,我們都會創(chuàng)建一個 result 數(shù)組 // 因為我們不想改變原始數(shù)組。 for (let i = 0, len = arr.length; i < len; i++) { result.push(mapCallback(arr[i], i, arr)); // 將 mapCallback 返回的結(jié)果 push 到 result 數(shù)組中 } return result; }Array.prototype.filter方法filter() 方法創(chuàng)建一個新數(shù)組, 其包含通過所提供函數(shù)實現(xiàn)的測試的所有元素。function filter(arr, filterCallback) {// 首先,檢查傳遞的參數(shù)是否正確。if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function'){return [];} else {let result = [];// 每次調(diào)用此函數(shù)時,我們都會創(chuàng)建一個 result 數(shù)組// 因為我們不想改變原始數(shù)組。for (let i = 0, len = arr.length; i < len; i++) {// 檢查 filterCallback 的返回值是否是真值if (filterCallback(arr[i], i, arr)) {// 如果條件為真,則將數(shù)組元素 push 到 result 中result.push(arr[i]);}}return result; // return the result array}}
Array.prototype.reduce方法reduce() 方法對數(shù)組中的每個元素執(zhí)行一個由您提供的reducer函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個返回值。function reduce(arr, reduceCallback, initialValue) { // 首先,檢查傳遞的參數(shù)是否正確。 if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function') { return []; } else { // 如果沒有將initialValue傳遞給該函數(shù),我們將使用第一個數(shù)組項作為initialValue let hasInitialValue = initialValue !== undefined; let value = hasInitialValue ? initialValue : arr[0]; 、
// 如果有傳遞 initialValue,則索引從 1 開始,否則從 0 開始 for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) { value = reduceCallback(value, arr[i], i, arr); } return value; }}arguments對象是函數(shù)中傳遞的參數(shù)值的集合。它是一個類似數(shù)組的對象,因為它有一個length屬性,我們可以使用數(shù)組索引表示法arguments[1]來訪問單個值,但它沒有數(shù)組中的內(nèi)置方法,如:forEach、reduce、filter和map。Array.prototype.slice將arguments對象轉(zhuǎn)換成一個數(shù)組。function one() {return Array.prototype.slice.call(arguments);}
arguments對象。function one() { return arguments;}const two = function () { return arguments;}const three = function three() { return arguments;}
const four = () => arguments;
four(); // Throws an error - arguments is not definedfour時,它會拋出一個ReferenceError: arguments is not defined error。使用rest語法,可以解決這個問題。const four = (...args) => args;
Object.create方法創(chuàng)建沒有原型的對象。const o1 = {};console.log(o1.toString()); // [object Object]
const o2 = Object.create(null);console.log(o2.toString());// throws an error o2.toString is not a functionb會變成一個全局變量?function myFunc() {let a = b = 0;}myFunc();
function myFunc() { let a = (b = 0);}
myFunc();b = 0求值,在本例中b沒有聲明。因此,JS引擎在這個函數(shù)外創(chuàng)建了一個全局變量b,之后表達式b = 0的返回值為0,并賦給新的局部變量a。function myFunc() {let a,b;a = b = 0;}myFunc();
箭頭函數(shù)
類
模板字符串
加強的對象字面量
對象解構(gòu)
Promise
生成器
模塊
Symbol
代理
Set
函數(shù)默認參數(shù)
rest 和展開
塊作用域
var,let和const的區(qū)別是什么?var聲明的變量會掛載在window上,而let和const聲明的變量不會:var a = 100;console.log(a,window.a); // 100 100
let b = 10;console.log(b,window.b); // 10 undefined
const c = 1;console.log(c,window.c); // 1 undefinedvar聲明變量存在變量提升,let和const不存在變量提升:console.log(a); // undefined ===> a已聲明還沒賦值,默認得到undefined值var a = 100;console.log(b); // 報錯:b is not defined ===> 找不到b這個變量let b = 10;console.log(c); // 報錯:c is not defined ===> 找不到c這個變量const c = 10;
let和const聲明形成塊作用域if(1){ var a = 100; let b = 10;}
console.log(a); // 100console.log(b) // 報錯:b is not defined ===> 找不到b這個變量
-------------------------------------------------------------
if(1){ var a = 100; const c = 1;}console.log(a); // 100console.log(c) // 報錯:c is not defined ===> 找不到c這個變量let和const不能聲明同名變量,而var可以var a = 100;console.log(a); // 100var a = 10;console.log(a); // 10-------------------------------------let a = 100;let a = 10;// 控制臺報錯:Identifier 'a' has already been declared ===> 標識符a已經(jīng)被聲明了。
var a = 100;
if(1){ a = 10; //在當前塊作用域中存在a使用let/const聲明的情況下,給a賦值10時,只會在當前作用域找變量a, // 而這時,還未到聲明時候,所以控制臺Error:a is not defined let a = 1;}/** 1、一旦聲明必須賦值,不能使用null占位。** 2、聲明后不能再修改** 3、如果聲明的是復(fù)合類型數(shù)據(jù),可以修改其屬性** */const a = 100;const list = [];list[0] = 10;console.log(list); // [10]const obj = {a:100};obj.name = 'apple';obj.a = 10000;console.log(obj); // {a:10000,name:'apple'}
this,arguments,super或new.target。箭頭函數(shù)表達式更適用于那些本來需要匿名函數(shù)的地方,并且它不能用作構(gòu)造函數(shù)。//ES5 Versionvar getCurrentDate = function (){ return new Date();}
//ES6 Versionconst getCurrentDate = () => new Date();function(){}聲明和return關(guān)鍵字,這兩個關(guān)鍵字分別是創(chuàng)建函數(shù)和返回值所需要的。在箭頭函數(shù)版本中,我們只需要()括號,不需要 return 語句,因為如果我們只有一個表達式或值需要返回,箭頭函數(shù)就會有一個隱式的返回。//ES5 Versionfunction greet(name) {return 'Hello ' + name + '!';}//ES6 Versionconst greet = (name) => `Hello ${name}`;const greet2 = name => `Hello ${name}`;
const getArgs = () => arguments
const getArgs2 = (...rest) => restarguments對象。所以調(diào)用第一個getArgs函數(shù)會拋出一個錯誤。相反,我們可以使用rest參數(shù)來獲得在箭頭函數(shù)中傳遞的所有參數(shù)。const data = {result: 0,nums: [1, 2, 3, 4, 5],computeResult() {// 這里的“this”指的是“data”對象const addAll = () => {return this.nums.reduce((total, cur) => total + cur, 0)};this.result = addAll();}};
this值。它捕獲詞法作用域函數(shù)的this值,在此示例中,addAll函數(shù)將復(fù)制computeResult 方法中的this值,如果我們在全局作用域聲明箭頭函數(shù),則this值為 window 對象。類(class)是在 JS 中編寫構(gòu)造函數(shù)的新方法。它是使用構(gòu)造函數(shù)的語法糖,在底層中使用仍然是原型和基于原型的繼承。 //ES5 Version function Person(firstName, lastName, age, address){ this.firstName = firstName; this.lastName = lastName; this.age = age; this.address = address; }
Person.self = function(){ return this; }
Person.prototype.toString = function(){ return '[object Person]'; }
Person.prototype.getFullName = function (){ return this.firstName + ' ' + this.lastName; }
//ES6 Version class Person { constructor(firstName, lastName, age, address){ this.lastName = lastName; this.firstName = firstName; this.age = age; this.address = address; }
static self() { return this; }
toString(){ return '[object Person]'; }
getFullName(){ return `${this.firstName} ${this.lastName}`; } }//ES5 VersionEmployee.prototype = Object.create(Person.prototype);function Employee(firstName, lastName, age, address, jobTitle, yearStarted) {Person.call(this, firstName, lastName, age, address);this.jobTitle = jobTitle;this.yearStarted = yearStarted;}Employee.prototype.describe = function () {return `I am ${this.getFullName()} and I have a position of ${this.jobTitle}and I started at ${this.yearStarted}`;}Employee.prototype.toString = function () {return '[object Employee]';}//ES6 Versionclass Employee extends Person { //Inherits from 'Person' classconstructor(firstName, lastName, age, address, jobTitle, yearStarted) {super(firstName, lastName, age, address);this.jobTitle = jobTitle;this.yearStarted = yearStarted;}describe() {return `I am ${this.getFullName()} and I have a position of ${this.jobTitle}and I started at ${this.yearStarted}`;}toString() { // Overriding the 'toString' method of 'Person'return '[object Employee]';}}
class Something {
}
function AnotherSomething(){
}const as = new AnotherSomething();const s = new Something();
console.log(typeof Something); // 'function'console.log(typeof AnotherSomething); // 'function'console.log(as.toString()); // '[object Object]'console.log(as.toString()); // '[object Object]'console.log(as.toString === Object.prototype.toString); // trueconsole.log(s.toString === Object.prototype.toString); // true//ES5 Versionvar greet = 'Hi I\'m Mark';//ES6 Versionlet greet = `Hi I'm Mark`;
//ES5 Versionvar lastWords = '\n' + ' I \n' + ' Am \n' + 'Iron Man \n';
//ES6 Versionlet lastWords = ` I Am Iron Man`;\n以在字符串中添加新行。在模板字符串中,我們不需要這樣做。//ES5 Versionfunction greet(name) {return 'Hello ' + name + '!';}//ES6 Versionfunction greet(name) {return `Hello ${name} !`;}
+運算符。在模板字符串s中,我們可以使用${expr}嵌入一個表達式,這使其比 ES5 版本更整潔。const employee = { firstName: 'Marko', lastName: 'Polo', position: 'Software Developer', yearHired: 2017};var firstName = employee.firstName;var lastName = employee.lastName;var position = employee.position;var yearHired = employee.yearHired;
{ firstName, lastName, position, yearHired } = employee;let { firstName: fName, lastName: lName, position, yearHired } = employee;
undefined 時,我們還可以指定默認值:let { firstName = 'Mark', lastName: lName, position, yearHired } = employee;CommonJS-Node.js
AMD(異步模塊定義)-瀏覽器
import用于從另一個文件中獲取功能或幾個功能或值,同時export用于從文件中公開功能或幾個功能或值。// 使用 ES5 CommonJS - helpers.jsexports.isNull = function (val) {return val === null;}exports.isUndefined = function (val) {return val === undefined;}exports.isNullOrUndefined = function (val) {return exports.isNull(val) || exports.isUndefined(val);}
// 使用 ES6 Modules - helpers.jsexport function isNull(val){ return val === null;}
export function isUndefined(val) { return val === undefined;}
export function isNullOrUndefined(val) { return isNull(val) || isUndefined(val);}// 使用 ES5 (CommonJS) - index.jsconst helpers = require('./helpers.js'); // helpers is an objectconst isNull = helpers.isNull;const isUndefined = helpers.isUndefined;const isNullOrUndefined = helpers.isNullOrUndefined;// or if your environment supports Destructuringconst { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js');-------------------------------------------------------// ES6 Modules - index.jsimport * as helpers from './helpers.js'; // helpers is an object// orimport { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js';// using 'as' for renaming named exports
// 使用 ES5 (CommonJS) - index.jsclass Helpers { static isNull(val) { return val === null; }
static isUndefined(val) { return val === undefined; }
static isNullOrUndefined(val) { return this.isNull(val) || this.isUndefined(val); }}
module.exports = Helpers;// 使用 ES6 Modules - helpers.jsclass Helpers {static isNull(val) {return val === null;}static isUndefined(val) {return val === undefined;}static isNullOrUndefined(val) {return this.isNull(val) || this.isUndefined(val);}}export default Helpers
// 使用 ES5 (CommonJS) - index.jsconst Helpers = require('./helpers.js');console.log(Helpers.isNull(null));import Helpers from '.helpers.js'console.log(Helpers.isNull(null));
Set對象,它是如何工作的?Set構(gòu)造函數(shù)創(chuàng)建Set實例。const set1 = new Set();const set2 = new Set(['a','b','c','d','d','e']);add方法向Set實例中添加一個新值,因為add方法返回Set對象,所以我們可以以鏈式的方式再次使用add。如果一個值已經(jīng)存在于Set對象中,那么它將不再被添加。set2.add('f');set2.add('g').add('h').add('i').add('j').add('k').add('k');// 后一個“k”不會被添加到set對象中,因為它已經(jīng)存在了
has方法檢查Set實例中是否存在特定的值。set2.has('a') // trueset2.has('z') // truesize屬性獲得Set實例的長度。set2.size // returns 10
clear方法刪除 Set 中的數(shù)據(jù)。set2.clear();Set對象來刪除數(shù)組中重復(fù)的元素。const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5];const uniqueNums = [...new Set(numbers)]; // [1,2,3,4,5,6,7,8]
const btnAdd = document.getElementById('btnAdd');
btnAdd.addEventListener('click', function clickCallback(e) { // do something useless});id為btnAdd的元素中的click事件,如果它被單擊,則執(zhí)行clickCallback函數(shù)。回調(diào)函數(shù)向某些數(shù)據(jù)或事件添加一些功能。reduce、filter和map方法需要一個回調(diào)作為參數(shù)。回調(diào)的一個很好的類比是,當你打電話給某人,如果他們不接,你留下一條消息,你期待他們回調(diào)。調(diào)用某人或留下消息的行為是事件或數(shù)據(jù),回調(diào)是你希望稍后發(fā)生的操作。promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結(jié)果。promise有三種狀態(tài):pending(等待態(tài)),fulfiled(成功態(tài)),rejected(失敗態(tài));狀態(tài)一旦改變,就不會再變。創(chuàng)造promise實例后,它會立即執(zhí)行。fs.readFile('somefile.txt', function (e, data) {if (e) {console.log(e);}console.log(data);});
// 回調(diào)地獄fs.readFile('somefile.txt', function (e, data) { //your code here fs.readdir('directory', function (e, files) { //your code here fs.mkdir('directory', function (e) { //your code here }) })})promise,它將更易于閱讀、理解和維護。promReadFile('file/path').then(data => {return promReaddir('directory');}).then(data => {return promMkdir('directory');}).catch(e => {console.log(e);})
promise有三種不同的狀態(tài):pending:初始狀態(tài),完成或失敗狀態(tài)的前一個狀態(tài)
fulfilled:操作成功完成
rejected:操作失敗
pending 狀態(tài)的 Promise 對象會觸發(fā) fulfilled/rejected 狀態(tài),在其狀態(tài)處理方法中可以傳入?yún)?shù)/失敗信息。當操作成功完成時,Promise 對象的 then 方法就會被調(diào)用;否則就會觸發(fā) catch。如:const myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve('成功!'); }, 250);});
myFirstPromise.then((data) => { console.log('Yay! ' + data);}).catch((e) => {...});async/await 及其如何工作?async/await是 JS 中編寫異步或非阻塞代碼的新方法。它建立在Promises之上,讓異步代碼的可讀性和簡潔度都更高。async/await是 JS 中編寫異步或非阻塞代碼的新方法。它建立在Promises之上,相對于 Promise 和回調(diào),它的可讀性和簡潔度都更高。但是,在使用此功能之前,我們必須先學(xué)習(xí)Promises的基礎(chǔ)知識,因為正如我之前所說,它是基于Promise構(gòu)建的,這意味著幕后使用仍然是Promise。function callApi() {return fetch('url/to/api/endpoint').then(resp => resp.json()).then(data => {//do something with 'data'}).catch(err => {//do something with 'err'});}
async/await,我們使用 tru/catch 語法來捕獲異常。async function callApi() { try { const resp = await fetch('url/to/api/endpoint'); const data = await resp.json(); //do something with 'data' } catch (e) { //do something with 'err' }}async關(guān)鍵聲明函數(shù)會隱式返回一個Promise。const giveMeOne = async () => 1;giveMeOne().then((num) => {console.log(num); // logs 1});
await關(guān)鍵字只能在async function中使用。在任何非async function的函數(shù)中使用await關(guān)鍵字都會拋出錯誤。await關(guān)鍵字在執(zhí)行下一行代碼之前等待右側(cè)表達式(可能是一個Promise)返回。const giveMeOne = async () => 1;
function getOne() { try { const num = await giveMeOne(); console.log(num); } catch (e) { console.log(e); }}
// Uncaught SyntaxError: await is only valid in async function
async function getTwo() { try { const num1 = await giveMeOne(); // 這行會等待右側(cè)表達式執(zhí)行完成 const num2 = await giveMeOne(); return num1 + num2; } catch (e) { console.log(e); }}
await getTwo(); // 2...),可以將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列。說的通俗易懂點,有點像化骨綿掌,把一個大元素給打散成一個個單獨的小元素。...)表示,它的樣子看起來和展開操作符一樣,但是它是用于解構(gòu)數(shù)組和對象。在某種程度上,剩余元素和展開元素相反,展開元素會“展開”數(shù)組變成多個元素,剩余元素會收集多個元素和“壓縮”成一個單一的元素。function add(a, b) {return a + b;};const nums = [5, 6];const sum = add(...nums);console.log(sum);
add函數(shù)時使用了展開操作符,對nums數(shù)組進行展開。所以參數(shù)a的值是5 ,b的值是6,所以sum 是11。function add(...rest) { return rest.reduce((total,current) => total + current);};
console.log(add(1, 2)); // 3console.log(add(1, 2, 3, 4, 5)); // 15add函數(shù),它接受任意數(shù)量的參數(shù),并將它們?nèi)肯嗉?,然后返回總?shù)。const [first, ...others] = [1, 2, 3, 4, 5];console.log(first); // 1console.log(others); // [2,3,4,5]
//ES5 Versionfunction add(a,b){ a = a || 0; b = b || 0; return a + b;}
//ES6 Versionfunction add(a = 0, b = 0){ return a + b;}add(1); // returns 1function getFirst([first, ...rest] = [0, 1]) {return first;}getFirst(); // 0getFirst([10,20,30]); // 10function getArr({ nums } = { nums: [1, 2, 3, 4] }){return nums;}getArr(); // [1, 2, 3, 4]getArr({nums:[5,4,3,2,1]}); // [5,4,3,2,1]
function doSomethingWithValue(value = 'Hello World', callback = () => { console.log(value) }) { callback();}doSomethingWithValue(); //'Hello World'Undefined,Null,Boolean,Number,String,Symbol,BigIntObject,Array,Date,RegExp等,說白了就是對象。let name = 'marko';console.log(typeof name); // 'string'console.log(name.toUpperCase()); // 'MARKO'
name類型是 string,屬于基本類型,所以它沒有屬性和方法,但是在這個例子中,我們調(diào)用了一個toUpperCase()方法,它不會拋出錯誤,還返回了對象的變量值。name變量的行為類似于對象。除null和undefined之外的每個基本類型都有自己包裝對象。也就是:String,Number,Boolean,Symbol和BigInt。在這種情況下,name.toUpperCase()在幕后看起來如下:console.log(new String(name).toUpperCase()); // 'MARKO'console.log(1 + '6'); // 16console.log(false + true); // 1console.log(6 * '2'); // 12
console.log語句結(jié)果為16。在其他語言中,這會拋出編譯時錯誤,但在 JS 中,1被轉(zhuǎn)換成字符串,然后與+運算符連接。我們沒有做任何事情,它是由 JS 自動完成。console.log語句結(jié)果為1,JS 將false轉(zhuǎn)換為boolean 值為 0,,true為1,因此結(jié)果為1。console.log語句結(jié)果12,它將'2'轉(zhuǎn)換為一個數(shù)字,然后乘以6 * 2,結(jié)果是12。console.log(1 + parseInt('6'));parseInt函數(shù)將'6'轉(zhuǎn)換為number ,然后使用+運算符將1和6相加。NaN表示“非數(shù)字”是 JS 中的一個值,該值是將數(shù)字轉(zhuǎn)換或執(zhí)行為非數(shù)字值的運算結(jié)果,因此結(jié)果為NaN。let a;console.log(parseInt('abc')); // NaNconsole.log(parseInt(null)); // NaNconsole.log(parseInt(undefined)); // NaNconsole.log(parseInt(++a)); // NaNconsole.log(parseInt({} * 10)); // NaNconsole.log(parseInt('abc' - 2)); // NaNconsole.log(parseInt(0 / 0)); // NaNconsole.log(parseInt('10a' * 10)); // NaN
isNaN方法,用于測試值是否為isNaN值,但是這個函數(shù)有一個奇怪的行為。console.log(isNaN()); // trueconsole.log(isNaN(undefined)); // trueconsole.log(isNaN({})); // trueconsole.log(isNaN(String('a'))); // trueconsole.log(isNaN(() => { })); // trueconsole.log語句都返回true,即使我們傳遞的值不是NaN。ES6中,建議使用Number.isNaN方法,因為它確實會檢查該值(如果確實是NaN),或者我們可以使自己的輔助函數(shù)檢查此問題,因為在 JS 中,NaN是唯一的值,它不等于自己。function checkIfNaN(value) {return value !== value;}
Array.isArray方法來檢查值是否為數(shù)組。當傳遞給它的參數(shù)是數(shù)組時,它返回true,否則返回false。console.log(Array.isArray(5)); // falseconsole.log(Array.isArray('')); // falseconsole.log(Array.isArray()); // falseconsole.log(Array.isArray(null)); // falseconsole.log(Array.isArray({ length: 5 })); // false
console.log(Array.isArray([])); // truepolyfill實現(xiàn)。function isArray(value){return Object.prototype.toString.call(value) === '[object Array]'}
let a = []if (a instanceof Array) { console.log('是數(shù)組')} else { console.log('非數(shù)組')}%模運算符的情況下檢查一個數(shù)字是否是偶數(shù)?&運算符,&對其操作數(shù)進行運算,并將其視為二進制值,然后執(zhí)行與運算。function isEven(num) {if (num & 1) {return false} else {return true}}
0 二進制數(shù)是 0001 二進制數(shù)是 0012 二進制數(shù)是 0103 二進制數(shù)是 0114 二進制數(shù)是 1005 二進制數(shù)是 1016 二進制數(shù)是 1107 二進制數(shù)是 111console.log(5&1)這個表達式時,結(jié)果為1。首先,&運算符將兩個數(shù)字都轉(zhuǎn)換為二進制,因此5變?yōu)?/span>101,1變?yōu)?/span>001。0和1)。 101&001,從表中可以看出,如果a & b為1,所以5&1結(jié)果為1。首先我們比較最左邊的1&0,結(jié)果是0。
然后我們比較中間的0&0,結(jié)果是0。
然后我們比較最后1&1,結(jié)果是1。
最后,得到一個二進制數(shù)001,對應(yīng)的十進制數(shù),即1。
console.log(4 & 1) 結(jié)果為0。知道4的最后一位是0,而0 & 1 將是0。如果你很難理解這一點,我們可以使用遞歸函數(shù)來解決此問題。function isEven(num) {
if (num < 0 || num === 1) return false;
if (num == 0) return true;return isEven(num - 2);
}in 操作符號:const o = {'prop' : 'bwahahah','prop2' : 'hweasa'};console.log('prop' in o); // trueconsole.log('prop1' in o); // false
hasOwnProperty 方法,hasOwnProperty() 方法會返回一個布爾值,指示對象自身屬性中是否具有指定的屬性(也就是,是否有指定的鍵)。console.log(o.hasOwnProperty('prop2')); // trueconsole.log(o.hasOwnProperty('prop1')); // falseobj['prop']。如果屬性存在,它將返回該屬性的值,否則將返回undefined。console.log(o['prop']); // 'bwahahah'console.log(o['prop1']); // undefined
HTML - 網(wǎng)頁結(jié)構(gòu)
CSS - 網(wǎng)頁的樣式
JavaScript - 操作網(wǎng)頁的行為和更新DOM
XMLHttpRequest API - 用于從服務(wù)器發(fā)送和獲取數(shù)據(jù)
PHP,Python,Nodejs - 某些服務(wù)器端語言
const o = { name: 'Mark', greeting() { return `Hi, I'm ${this.name}`; } };
o.greeting(); //returns 'Hi, I'm Mark'function Person(name) {this.name = name;}Person.prototype.greeting = function () {return `Hi, I'm ${this.name}`;}const mark = new Person('Mark');mark.greeting(); //returns 'Hi, I'm Mark'
const n = { greeting() { return `Hi, I'm ${this.name}`; }};
const o = Object.create(n); // sets the prototype of 'o' to be 'n'
o.name = 'Mark';
console.log(o.greeting()); // logs 'Hi, I'm Mark'Object.freeze() 方法可以凍結(jié)一個對象。一個被凍結(jié)的對象再也不能被修改;凍結(jié)了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結(jié)一個對象后該對象的原型也不能被修改。freeze() 返回和傳入的參數(shù)相同的對象。ES5新增。
對象不可能擴展,也就是不能再添加新的屬性或者方法。
對象已有屬性不允許被刪除。
對象屬性特性不可以重新配置。
Object.seal方法生成的密封對象,如果屬性是可寫的,那么可以修改屬性值。
Object.freeze方法生成的凍結(jié)對象,屬性都是不可寫的,也就是屬性值無法更改。
in 運算符和 Object.hasOwnProperty 方法有什么區(qū)別?hasOwnPropert()方法返回值是一個布爾值,指示對象自身屬性中是否具有指定的屬性,因此這個方法會忽略掉那些從原型鏈上繼承到的屬性。Object.prototype.phone= '15345025546';let obj = {name: '西門大官人',age: '28'}console.log(obj.hasOwnProperty('phone')) // falseconsole.log(obj.hasOwnProperty('name')) // true
phone,hasOwnProperty方法會直接忽略掉。in 運算符返回true。console.log('phone' in obj) // truein運算符會檢查它或者其原型鏈是否包含具有指定名稱的屬性。回調(diào)
Promise
async/await
還有一些庫:async.js, bluebird, q, co
hoistedFunc();notHoistedFunc();function hoistedFunc(){console.log('注意:我會被提升');}var notHoistedFunc = function(){console.log('注意:我沒有被提升');}
notHoistedFunc調(diào)用拋出異常:Uncaught TypeError: notHoistedFunc is not a function,而hoistedFunc調(diào)用不會,因為hoistedFunc會被提升到作用域的頂部,而notHoistedFunc 不會。作為函數(shù)調(diào)用——如果一個函數(shù)沒有作為方法、構(gòu)造函數(shù)、apply、call 調(diào)用時,此時 this 指向的是 window 對象(非嚴格模式)
//Global Scope
function add(a,b){ console.log(this); return a + b; }
add(1,5); // 打印 'window' 對象和 6
const o = { method(callback){ callback(); } }
o.method(function (){ console.log(this); // 打印 'window' 對象 });作為方法調(diào)用——如果一個對象的屬性有一個函數(shù)的值,我們就稱它為方法。調(diào)用該方法時,該方法的this值指向該對象。
const details = {name : 'Marko',getName(){return this.name;}}details.getName(); // Marko// the 'this' value inside 'getName' method will be the 'details' object
作為構(gòu)造函數(shù)的調(diào)用-如果在函數(shù)之前使用new關(guān)鍵字調(diào)用了函數(shù),則該函數(shù)稱為構(gòu)造函數(shù)。構(gòu)造函數(shù)里面會默認創(chuàng)建一個空對象,并將this指向該對象。
function Employee(name, position, yearHired) { // creates an empty object {} // then assigns the empty object to the 'this' keyword // this = {}; this.name = name; this.position = position; this.yearHired = yearHired; // inherits from Employee.prototype // returns the 'this' value implicitly if no // explicit return statement is specified};
const emp = new Employee('Marko Polo', 'Software Developer', 2017);使用apply和call方法調(diào)用——如果我們想顯式地指定一個函數(shù)的this值,我們可以使用這些方法,這些方法對所有函數(shù)都可用。
const obj1 = {result:0};const obj2 = {result:0};function reduceAdd(){let result = 0;for(let i = 0, len = arguments.length; i < len; i++){result += arguments[i];}this.result = result;}reduceAdd.apply(obj1, [1, 2, 3, 4, 5]);// reduceAdd 函數(shù)中的 this 對象將是 obj1reduceAdd.call(obj2, 1, 2, 3, 4, 5);// reduceAdd 函數(shù)中的 this 對象將是 obj2
function memoize(fn) { const cache = {}; return function (param) { if (cache[param]) { console.log('cached'); return cache[param]; } else { let result = fn(param); cache[param] = result; console.log(`not cached`); return result; } }}
const toUpper = (str ='')=> str.toUpperCase();
const toUpperMemoized = memoize(toUpper);
toUpperMemoized('abcdef');toUpperMemoized('abcdef');const slice = Array.prototype.slice;function memoize(fn) {const cache = {};return (...args) => {const params = slice.call(args);console.log(params);if (cache[params]) {console.log('cached');return cache[params];} else {let result = fn(...args);cache[params] = result;console.log(`not cached`);return result;}}}const makeFullName = (fName, lName) => `${fName} ${lName}`;const reduceAdd = (numbers, startingValue = 0) =>numbers.reduce((total, cur) => total + cur, startingValue);const memoizedMakeFullName = memoize(makeFullName);const memoizedReduceAdd = memoize(reduceAdd);memoizedMakeFullName('Marko', 'Polo');memoizedMakeFullName('Marko', 'Polo');memoizedReduceAdd([1, 2, 3, 4, 5], 5);memoizedReduceAdd([1, 2, 3, 4, 5], 5);
typeof null == 'object'總是返回true,因為這是自 JS 誕生以來null的實現(xiàn)。曾經(jīng)有人提出將typeof null == 'object'修改為typeof null == 'null',但是被拒絕了,因為這將導(dǎo)致更多的bug。===來檢查值是否為null。function isNull(value){ return value === null;}new關(guān)鍵字與構(gòu)造函數(shù)一起使用以創(chuàng)建對象在JavaScript中。function Employee(name, position, yearHired) {this.name = name;this.position = position;this.yearHired = yearHired;};const emp = new Employee('Marko Polo', 'Software Developer', 2017);
new關(guān)鍵字做了4件事:創(chuàng)建空對象 {}
將空對象分配給 this 值
將空對象的__proto__指向構(gòu)造函數(shù)的prototype
如果沒有使用顯式return語句,則返回this
|
|