1.undefined 和 null 有什么區(qū)別?undefined 和null 之間的差異之前,我們先來看看它們的相似類。let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint']; Boolean(value) 或!!value 將其轉(zhuǎn)換為布爾值時(shí),值為false 。
undefined 是未指定特定值的變量的默認(rèn)值,或者沒有顯式返回值的函數(shù),如:console.log(1) ,還包括對象中不存在的屬性,這些 JS 引擎都會為其分配 undefined 值。let _thisIsUndefined; const doNothing = () => {}; const someObj = { a : 'ay', b : 'bee', c : 'si' };
console.log(_thisIsUndefined); // undefined console.log(doNothing()); // undefined console.log(someObj['d']); // undefined null 是“不代表任何值的值”。 null 是已明確定義給變量的值。在此示例中,當(dāng)fs.readFile 方法未引發(fā)錯(cuò)誤時(shí),我們將獲得null 值。
null 和undefined 時(shí),我們使用== 時(shí)得到true ,使用=== 時(shí)得到false : console.log(null == undefined); // true console.log(null === undefined); // false 2. && 運(yùn)算符能做什么&& 也可以叫邏輯與,在其操作數(shù)中找到第一個(gè)虛值表達(dá)式并返回它,如果沒有找到任何虛值表達(dá)式,則返回最后一個(gè)真值表達(dá)式。它采用短路來防止不必要的工作。
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. || 運(yùn)算符能做什么|| 也叫或邏輯或 ,在其操作數(shù)中找到第一個(gè)真值表達(dá)式并返回它。這也使用了短路來防止不必要的工作。在支持 ES6 默認(rèn)函數(shù)參數(shù)之前,它用于初始化函數(shù)中的默認(rèn)參數(shù)值。console.log(null || 1 || undefined); // 1
function logName(name) { var n = name || 'Mark'; console.log(n); }
logName(); // 'Mark' 4. 使用 + 或一元加運(yùn)算符是將字符串轉(zhuǎn)換為數(shù)字的最快方法嗎?+ 是將字符串轉(zhuǎn)換為數(shù)字的最快方法,因?yàn)槿绻狄呀?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 方法具有第三個(gè)可選參數(shù)useCapture ,其默認(rèn)值為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 方法具有第三個(gè)可選參數(shù)useCapture ,其默認(rèn)值為false ,事件將在冒泡階段中發(fā)生,如果為true ,則事件將在捕獲階段中發(fā)生。如果單擊child 元素,它將分別在控制臺上打印window ,document ,html ,grandparent 和parent ,這就是事件捕獲。9. event.preventDefault() 和 event.stopPropagation()方法之間有什么區(qū)別?event.preventDefault() 方法可防止元素的默認(rèn)行為。如果在表單元素中使用,它將阻止其提交。如果在錨元素中使用,它將阻止其導(dǎo)航。如果在上下文菜單中使用,它將阻止其顯示或顯示。 event.stopPropagation() 方法用于阻止捕獲和冒泡階段中當(dāng)前事件的進(jìn)一步傳播。10. 如何知道是否在元素中使用了 |
x | y | x==y |
true
。條件1
,因?yàn)?/span>x
和y
具有相同的類型和值。條件4
,在比較之前將y
轉(zhuǎn)換為數(shù)字。條件2
。條件7
,因?yàn)?/span>y
是boolean
類型。條件8
。使用toString()
方法將數(shù)組轉(zhuǎn)換為字符串,該方法返回1,2
。條件8
。使用toString()
方法將對象轉(zhuǎn)換為字符串,該方法返回[object Object]
。x | y | x ===y |
===
運(yùn)算符,則第一個(gè)示例以外的所有比較將返回false
,因?yàn)樗鼈兊念愋筒煌谝粋€(gè)示例將返回true
,因?yàn)閮烧叩念愋秃椭迪嗤?/span>let a = { a: 1 };
let b = { a: 1 };
let c = a;
console.log(a === b); // 打印 false,即使它們有相同的屬性
console.log(a === c); // true
console.log
語句返回false
,而第二個(gè)console.log
語句返回true
。a
和c
有相同的引用地址,而a
和b
沒有。!!
運(yùn)算符可以將右側(cè)的值強(qiáng)制轉(zhuǎn)換為布爾值,這也是將值轉(zhuǎn)換為布爾值的一種簡單方法。console.log(!!null); // false
console.log(!!undefined); // false
console.log(!!''); // false
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!' '); // true
console.log(!!{}); // true
console.log(!![]); // true
console.log(!!1); // true
console.log(!![].length); // false
逗號
運(yùn)算符在一行中計(jì)算多個(gè)表達(dá)式。它從左到右求值,并返回右邊最后一個(gè)項(xiàng)目或最后一個(gè)操作數(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
,此時(shí)x
的值為11
。之后,將x
的當(dāng)前值乘以2
并將其分配給x
,x
的更新值為22
。然后,將x
的當(dāng)前值減去5
并將結(jié)果分配給x
x
更新后的值為17
。最后,我們將x
的值增加10
,然后將更新的值分配給x
,最終x
的值為27
。編譯
和執(zhí)行
。var
關(guān)鍵字進(jìn)行聲明),還會為它們提供默認(rèn)值: undefined
。var
聲明的變量,或者函數(shù)聲明才會被提升,相反,函數(shù)表達(dá)式或箭頭函數(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; // 默認(rèn)值 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 namespace
var 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 defined
console.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ù)時(shí),全局作用域是a
閉包的一部分。globalVar
在圖中沒有值的原因是該變量的值可以根據(jù)調(diào)用函數(shù)a
的位置和時(shí)間而改變。但是在上面的示例中,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
時(shí),即使我們?yōu)?/span>outerVar
變量分配了新值outer-2
,outerParam
也繼續(xù)保留outer
值,因?yàn)橹匦路峙涫窃谡{(diào)用outerFunc
之后發(fā)生的,并且當(dāng)我們調(diào)用outerFunc
函數(shù)時(shí),它會在作用域鏈中查找outerVar
的值,此時(shí)的outerVar
的值將為 'outer'
。innerFunc
的x
變量時(shí),innerParam
將具有一個(gè)inner
值,因?yàn)檫@是我們在調(diào)用中傳遞的值,而globalVar
變量值為guess
,因?yàn)樵谡{(diào)用x
變量之前,我們將一個(gè)新值分配給globalVar
。const arrFuncs = [];
for(var i = 0; i < 5; i++){
arrFuncs.push(function (){
return i;
});
}
console.log(i); // i is 5
for (let i = 0; i < arrFuncs.length; i++) {
console.log(arrFuncs[i]()); // 都打印 5
}
var
關(guān)鍵字創(chuàng)建一個(gè)全局變量,當(dāng)我們 push 一個(gè)函數(shù)時(shí),這里返回的全局變量i
。因此,當(dāng)我們在循環(huán)后在該數(shù)組中調(diào)用其中一個(gè)函數(shù)時(shí),它會打印5
,因?yàn)槲覀兊玫?/span>i
的當(dāng)前值為5
,我們可以訪問它,因?yàn)樗侨肿兞俊?/span>let
來代替 var
的聲明。const falsyValues = ['', 0, null, undefined, NaN, false];
false
的值。Boolean
函數(shù)或者 !!
運(yùn)算符。'use strict'
是 ES5 特性,它使我們的代碼在函數(shù)或整個(gè)腳本中處于嚴(yán)格模式。嚴(yán)格模式幫助我們在代碼的早期避免 bug,并為其添加限制。變量必須聲明后再使用
函數(shù)的參數(shù)不能有同名屬性,否則報(bào)錯(cuò)
不能使用with
語句
不能對只讀屬性賦值,否則報(bào)錯(cuò)
不能使用前綴 0 表示八進(jìn)制數(shù),否則報(bào)錯(cuò)
不能刪除不可刪除的屬性,否則報(bào)錯(cuò)
不能刪除變量delete prop
,會報(bào)錯(cuò),只能刪除屬性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語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;
提高編譯器效率,增加運(yùn)行速度;
為未來新版本的Javascript做好鋪墊。
this
值是什么?this
指的是當(dāng)前正在執(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
對象,該對象當(dāng)前是執(zhí)行函數(shù)的“所有者”對象。var name = 'Ford Ranger';
var getCarName = carDetails.getName;
console.log(getCarName()); // Ford Ranger
Ford Ranger
,這很奇怪,因?yàn)樵诘谝粋€(gè)console.log
語句中打印的是Ford Mustang
。這樣做的原因是getCarName
方法有一個(gè)不同的“所有者”對象,即window
對象。在全局作用域中使用var
關(guān)鍵字聲明變量會在window
對象中附加與變量名稱相同的屬性。請記住,當(dāng)沒有使用“use strict”
時(shí),在全局作用域中this
指的是window
對象。console.log(getCarName === window.getCarName); // true
console.log(getCarName === this.getCarName); // true
this
和window
引用同一個(gè)對象。apply
和call
方法。console.log(getCarName.apply(carDetails)); // Ford Mustang
console.log(getCarName.call(carDetails)); // Ford Mustang
apply
和call
方法期望第一個(gè)參數(shù)是一個(gè)對象,該對象是函數(shù)內(nèi)部this
的值。IIFE
或立即執(zhí)行的函數(shù)表達(dá)式,在全局作用域內(nèi)聲明的函數(shù),對象內(nèi)部方法中的匿名函數(shù)和內(nèi)部函數(shù)的this
具有默認(rèn)值,該值指向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 function
console.log(this.name);
}
getName();
},
name: 'Marko Polo',
thisIsAnnoying(callback){
callback();
}
};
this
。它復(fù)制了這個(gè)封閉的詞法作用域中this
值,在這個(gè)例子中,this
值在getName
內(nèi)部函數(shù)之外,也就是myFavoriteObj
對象。const o = {};
console.log(o.toString()); // logs [object Object]
o
對象中不存在o.toString
方法,它也不會引發(fā)錯(cuò)誤,而是返回字符串[object Object]
。當(dāng)對象中不存在屬性時(shí),它將查看其原型,如果仍然不存在,則將其查找到原型的原型,依此類推,直到在原型鏈中找到具有相同屬性的屬性為止。原型鏈的末尾是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),然后再用另一個(gè)括號()
調(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ù),但是這個(gè)庫有兩個(gè)方法我們沒有使用:createGraph
和drawGraph
,因?yàn)檫@些方法都有bug
。我們想實(shí)現(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ù)返回一個(gè)包含兩個(gè)方法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
個(gè)li
子元素。當(dāng)我們單擊單個(gè)li
元素時(shí),打印對應(yīng)的下標(biāo)值。但在此外上述代碼不起作用,這里每次點(diǎn)擊 li
打印 i
的值都是5
,這是由于閉包的原因。var
關(guān)鍵字聲明變量時(shí),就創(chuàng)建全局變量i
。因此,當(dāng)我們單擊li
元素時(shí),它將打印5
,因?yàn)檫@是稍后在回調(diào)函數(shù)中引用它時(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時(shí),每次迭代的currentIndex
值都是不同的。apply()
方法調(diào)用一個(gè)具有給定this值的函數(shù),以及作為一個(gè)數(shù)組(或類似數(shù)組對象)提供的參數(shù)。const details = {
message: 'Hello World!'
};
function getMessage(){
return this.message;
}
getMessage.apply(details); // 'Hello World!'
call()
方法的作用和 apply()
方法類似,區(qū)別就是call()
方法接受的是參數(shù)列表,而apply()
方法接受的是一個(gè)參數(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()
方法使用一個(gè)指定的 this
值和單獨(dú)給出的一個(gè)或多個(gè)參數(shù)來調(diào)用一個(gè)函數(shù)。const details = {
message: 'Hello World!'
};
function getMessage(){
return this.message;
}
getMessage.call(details); // 'Hello World!'
apply()
方法類似,只有一個(gè)區(qū)別,就是 call()
方法接受的是一個(gè)參數(shù)列表,而 apply()
方法接受的是一個(gè)包含多個(gè)參數(shù)的數(shù)組。const person = {
name: 'Marko Polo'
};
function greeting(greetingMessage) {
return `${greetingMessage} ${this.name}`;
}
greeting.call(person, 'Hello'); // 'Hello Marko Polo!'
apply()
方法可以在使用一個(gè)指定的 this
值和一個(gè)參數(shù)數(shù)組(或類數(shù)組對象)的前提下調(diào)用某個(gè)函數(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]); // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15
bind()
方法創(chuàng)建一個(gè)新的函數(shù),在 bind()
被調(diào)用時(shí),這個(gè)新函數(shù)的 this
被指定為 bind()
的第一個(gè)參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時(shí)使用。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ù)還充當(dāng)了類的構(gòu)造函數(shù)的作用,同時(shí)又是一個(gè)Function
類的實(shí)例(instance)。這樣的多重身份讓JavaScript的函數(shù)變得非常重要。map()
方法創(chuàng)建一個(gè)新數(shù)組,其結(jié)果是該數(shù)組中的每個(gè)元素都調(diào)用一個(gè)提供的函數(shù)后返回的結(jié)果。function map(arr, mapCallback) {
// 首先,檢查傳遞的參數(shù)是否正確。
if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
return [];
} else {
let result = [];
// 每次調(diào)用此函數(shù)時(shí),我們都會創(chuàng)建一個(gè) result 數(shù)組
// 因?yàn)槲覀儾幌敫淖冊紨?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)建一個(gè)新數(shù)組, 其包含通過所提供函數(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ù)時(shí),我們都會創(chuàng)建一個(gè) result 數(shù)組
// 因?yàn)槲覀儾幌敫淖冊紨?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ù)組中的每個(gè)元素執(zhí)行一個(gè)由您提供的reducer
函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個(gè)返回值。function reduce(arr, reduceCallback, initialValue) {
// 首先,檢查傳遞的參數(shù)是否正確。
if (!Array.isArray(arr) || !arr.length || typeof reduceCallback !== 'function')
{
return [];
} else {
// 如果沒有將initialValue傳遞給該函數(shù),我們將使用第一個(gè)數(shù)組項(xiàng)作為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ù)值的集合。它是一個(gè)類似數(shù)組的對象,因?yàn)樗幸粋€(gè)length屬性,我們可以使用數(shù)組索引表示法arguments[1]
來訪問單個(gè)值,但它沒有數(shù)組中的內(nèi)置方法,如:forEach
、reduce
、filter
和map
。Array.prototype.slice
將arguments
對象轉(zhuǎn)換成一個(gè)數(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 defined
four
時(shí),它會拋出一個(gè)ReferenceError: arguments is not defined error
。使用rest
語法,可以解決這個(gè)問題。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 function
b
會變成一個(gè)全局變量?function myFunc() {
let a = b = 0;
}
myFunc();
function myFunc() {
let a = (b = 0);
}
myFunc();
b = 0
求值,在本例中b
沒有聲明。因此,JS引擎在這個(gè)函數(shù)外創(chuàng)建了一個(gè)全局變量b
,之后表達(dá)式b = 0
的返回值為0
,并賦給新的局部變量a
。function myFunc() {
let a,b;
a = b = 0;
}
myFunc();
箭頭函數(shù)
類
模板字符串
加強(qiáng)的對象字面量
對象解構(gòu)
Promise
生成器
模塊
Symbol
代理
Set
函數(shù)默認(rèn)參數(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 undefined
var
聲明變量存在變量提升,let
和const
不存在變量提升:console.log(a); // undefined ===> a已聲明還沒賦值,默認(rèn)得到undefined值
var a = 100;
console.log(b); // 報(bào)錯(cuò):b is not defined ===> 找不到b這個(gè)變量
let b = 10;
console.log(c); // 報(bào)錯(cuò):c is not defined ===> 找不到c這個(gè)變量
const c = 10;
let
和const
聲明形成塊作用域if(1){
var a = 100;
let b = 10;
}
console.log(a); // 100
console.log(b) // 報(bào)錯(cuò):b is not defined ===> 找不到b這個(gè)變量
-------------------------------------------------------------
if(1){
var a = 100;
const c = 1;
}
console.log(a); // 100
console.log(c) // 報(bào)錯(cuò):c is not defined ===> 找不到c這個(gè)變量
let
和const
不能聲明同名變量,而var
可以var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
-------------------------------------
let a = 100;
let a = 10;
// 控制臺報(bào)錯(cuò):Identifier 'a' has already been declared ===> 標(biāo)識符a已經(jīng)被聲明了。
var a = 100;
if(1){
a = 10;
//在當(dāng)前塊作用域中存在a使用let/const聲明的情況下,給a賦值10時(shí),只會在當(dāng)前作用域找變量a,
// 而這時(shí),還未到聲明時(shí)候,所以控制臺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ù)表達(dá)式更適用于那些本來需要匿名函數(shù)的地方,并且它不能用作構(gòu)造函數(shù)。//ES5 Version
var getCurrentDate = function (){
return new Date();
}
//ES6 Version
const getCurrentDate = () => new Date();
function(){}
聲明和return
關(guān)鍵字,這兩個(gè)關(guān)鍵字分別是創(chuàng)建函數(shù)和返回值所需要的。在箭頭函數(shù)版本中,我們只需要()
括號,不需要 return
語句,因?yàn)槿绻覀冎挥幸粋€(gè)表達(dá)式或值需要返回,箭頭函數(shù)就會有一個(gè)隱式的返回。//ES5 Version
function greet(name) {
return 'Hello ' + name + '!';
}
//ES6 Version
const greet = (name) => `Hello ${name}`;
const greet2 = name => `Hello ${name}`;
const getArgs = () => arguments
const getArgs2 = (...rest) => rest
arguments
對象。所以調(diào)用第一個(gè)getArgs
函數(shù)會拋出一個(gè)錯(cuò)誤。相反,我們可以使用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 Version
Employee.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 Version
class Employee extends Person { //Inherits from 'Person' class
constructor(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); // true
console.log(s.toString === Object.prototype.toString); // true
//ES5 Version
var greet = 'Hi I\'m Mark';
//ES6 Version
let greet = `Hi I'm Mark`;
//ES5 Version
var lastWords = '\n'
+ ' I \n'
+ ' Am \n'
+ 'Iron Man \n';
//ES6 Version
let lastWords = `
I
Am
Iron Man
`;
\n
以在字符串中添加新行。在模板字符串中,我們不需要這樣做。//ES5 Version
function greet(name) {
return 'Hello ' + name + '!';
}
//ES6 Version
function greet(name) {
return `Hello ${name} !`;
}
+
運(yùn)算符。在模板字符串s中,我們可以使用${expr}
嵌入一個(gè)表達(dá)式,這使其比 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
時(shí),我們還可以指定默認(rèn)值:let { firstName = 'Mark', lastName: lName, position, yearHired } = employee;
CommonJS-Node.js
AMD(異步模塊定義)-瀏覽器
import
用于從另一個(gè)文件中獲取功能或幾個(gè)功能或值,同時(shí)export
用于從文件中公開功能或幾個(gè)功能或值。// 使用 ES5 CommonJS - helpers.js
exports.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.js
export 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.js
const helpers = require('./helpers.js'); // helpers is an object
const isNull = helpers.isNull;
const isUndefined = helpers.isUndefined;
const isNullOrUndefined = helpers.isNullOrUndefined;
// or if your environment supports Destructuring
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js');
-------------------------------------------------------
// ES6 Modules - index.js
import * as helpers from './helpers.js'; // helpers is an object
// or
import { isNull, isUndefined, isNullOrUndefined as isValid } from './helpers.js';
// using 'as' for renaming named exports
// 使用 ES5 (CommonJS) - index.js
class 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.js
class 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.js
const 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
實(shí)例。const set1 = new Set();
const set2 = new Set(['a','b','c','d','d','e']);
add
方法向Set
實(shí)例中添加一個(gè)新值,因?yàn)?/span>add
方法返回Set
對象,所以我們可以以鏈?zhǔn)降姆绞皆俅问褂?/span>add
。如果一個(gè)值已經(jīng)存在于Set
對象中,那么它將不再被添加。set2.add('f');
set2.add('g').add('h').add('i').add('j').add('k').add('k');
// 后一個(gè)“k”不會被添加到set對象中,因?yàn)樗呀?jīng)存在了
has
方法檢查Set
實(shí)例中是否存在特定的值。set2.has('a') // true
set2.has('z') // true
size
屬性獲得Set
實(shí)例的長度。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
方法需要一個(gè)回調(diào)作為參數(shù)?;卣{(diào)的一個(gè)很好的類比是,當(dāng)你打電話給某人,如果他們不接,你留下一條消息,你期待他們回調(diào)。調(diào)用某人或留下消息的行為是事件或數(shù)據(jù),回調(diào)是你希望稍后發(fā)生的操作。promise
是一個(gè)對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時(shí)間會給你一個(gè)結(jié)果。promise
有三種狀態(tài):pending(等待態(tài))
,fulfiled(成功態(tài))
,rejected(失敗態(tài))
;狀態(tài)一旦改變,就不會再變。創(chuàng)造promise
實(shí)例后,它會立即執(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
,它將更易于閱讀、理解和維護(hù)。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)的前一個(gè)狀態(tài)
fulfilled:操作成功完成
rejected:操作失敗
pending
狀態(tài)的 Promise
對象會觸發(fā) fulfilled/rejected
狀態(tài),在其狀態(tài)處理方法中可以傳入?yún)?shù)/失敗信息。當(dāng)操作成功完成時(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ǔ)知識,因?yàn)檎缥抑八f,它是基于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ù)會隱式返回一個(gè)Promise。const giveMeOne = async () => 1;
giveMeOne()
.then((num) => {
console.log(num); // logs 1
});
await
關(guān)鍵字只能在async function
中使用。在任何非async function的函數(shù)中使用await
關(guān)鍵字都會拋出錯(cuò)誤。await
關(guān)鍵字在執(zhí)行下一行代碼之前等待右側(cè)表達(dá)式(可能是一個(gè)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è)表達(dá)式執(zhí)行完成
const num2 = await giveMeOne();
return num1 + num2;
} catch (e) {
console.log(e);
}
}
await getTwo(); // 2
...
),可以將一個(gè)數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列。說的通俗易懂點(diǎn),有點(diǎn)像化骨綿掌,把一個(gè)大元素給打散成一個(gè)個(gè)單獨(dú)的小元素。...
)表示,它的樣子看起來和展開操作符一樣,但是它是用于解構(gòu)數(shù)組和對象。在某種程度上,剩余元素和展開元素相反,展開元素會“展開”數(shù)組變成多個(gè)元素,剩余元素會收集多個(gè)元素和“壓縮”成一個(gè)單一的元素。function add(a, b) {
return a + b;
};
const nums = [5, 6];
const sum = add(...nums);
console.log(sum);
add
函數(shù)時(shí)使用了展開操作符,對nums
數(shù)組進(jìn)行展開。所以參數(shù)a
的值是5
,b
的值是6
,所以sum
是11
。function add(...rest) {
return rest.reduce((total,current) => total + current);
};
console.log(add(1, 2)); // 3
console.log(add(1, 2, 3, 4, 5)); // 15
add
函數(shù),它接受任意數(shù)量的參數(shù),并將它們?nèi)肯嗉樱缓蠓祷乜倲?shù)。const [first, ...others] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(others); // [2,3,4,5]
//ES5 Version
function add(a,b){
a = a || 0;
b = b || 0;
return a + b;
}
//ES6 Version
function add(a = 0, b = 0){
return a + b;
}
add(1); // returns 1
function getFirst([first, ...rest] = [0, 1]) {
return first;
}
getFirst(); // 0
getFirst([10,20,30]); // 10
function 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
,BigInt
Object
,Array
,Date
,RegExp
等,說白了就是對象。let name = 'marko';
console.log(typeof name); // 'string'
console.log(name.toUpperCase()); // 'MARKO'
name
類型是 string
,屬于基本類型,所以它沒有屬性和方法,但是在這個(gè)例子中,我們調(diào)用了一個(gè)toUpperCase()
方法,它不會拋出錯(cuò)誤,還返回了對象的變量值。name
變量的行為類似于對象。除null
和undefined
之外的每個(gè)基本類型都有自己包裝對象。也就是:String
,Number
,Boolean
,Symbol
和BigInt
。在這種情況下,name.toUpperCase()
在幕后看起來如下:console.log(new String(name).toUpperCase()); // 'MARKO'
console.log(1 + '6'); // 16
console.log(false + true); // 1
console.log(6 * '2'); // 12
console.log
語句結(jié)果為16
。在其他語言中,這會拋出編譯時(shí)錯(cuò)誤,但在 JS 中,1
被轉(zhuǎn)換成字符串,然后與+運(yùn)
算符連接。我們沒有做任何事情,它是由 JS 自動完成。console.log
語句結(jié)果為1
,JS 將false
轉(zhuǎn)換為boolean
值為 0
,,true
為1
,因此結(jié)果為1
。console.log
語句結(jié)果12
,它將'2'
轉(zhuǎn)換為一個(gè)數(shù)字,然后乘以6 * 2
,結(jié)果是12。console.log(1 + parseInt('6'));
parseInt
函數(shù)將'6'
轉(zhuǎn)換為number
,然后使用+
運(yùn)算符將1
和6
相加。NaN
表示“非數(shù)字”是 JS 中的一個(gè)值,該值是將數(shù)字轉(zhuǎn)換或執(zhí)行為非數(shù)字值的運(yùn)算結(jié)果,因此結(jié)果為NaN
。let a;
console.log(parseInt('abc')); // NaN
console.log(parseInt(null)); // NaN
console.log(parseInt(undefined)); // NaN
console.log(parseInt(++a)); // NaN
console.log(parseInt({} * 10)); // NaN
console.log(parseInt('abc' - 2)); // NaN
console.log(parseInt(0 / 0)); // NaN
console.log(parseInt('10a' * 10)); // NaN
isNaN
方法,用于測試值是否為isNaN值,但是這個(gè)函數(shù)有一個(gè)奇怪的行為。console.log(isNaN()); // true
console.log(isNaN(undefined)); // true
console.log(isNaN({})); // true
console.log(isNaN(String('a'))); // true
console.log(isNaN(() => { })); // true
console.log
語句都返回true
,即使我們傳遞的值不是NaN
。ES6
中,建議使用Number.isNaN
方法,因?yàn)樗_實(shí)會檢查該值(如果確實(shí)是NaN
),或者我們可以使自己的輔助函數(shù)檢查此問題,因?yàn)樵?JS 中,NaN是唯一的值,它不等于自己。function checkIfNaN(value) {
return value !== value;
}
Array.isArray
方法來檢查值是否為數(shù)組。當(dāng)傳遞給它的參數(shù)是數(shù)組時(shí),它返回true
,否則返回false
。console.log(Array.isArray(5)); // false
console.log(Array.isArray('')); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false
console.log(Array.isArray([])); // true
polyfill
實(shí)現(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ù)組')
}
%
模運(yùn)算符的情況下檢查一個(gè)數(shù)字是否是偶數(shù)?&
運(yùn)算符,&
對其操作數(shù)進(jìn)行運(yùn)算,并將其視為二進(jìn)制值,然后執(zhí)行與運(yùn)算。function isEven(num) {
if (num & 1) {
return false
} else {
return true
}
}
0
二進(jìn)制數(shù)是 000
1
二進(jìn)制數(shù)是 001
2
二進(jìn)制數(shù)是 010
3
二進(jìn)制數(shù)是 011
4
二進(jìn)制數(shù)是 100
5
二進(jìn)制數(shù)是 101
6
二進(jìn)制數(shù)是 110
7
二進(jìn)制數(shù)是 111
console.log(5&1)
這個(gè)表達(dá)式時(shí),結(jié)果為1
。首先,&
運(yùn)算符將兩個(gè)數(shù)字都轉(zhuǎn)換為二進(jì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
。
最后,得到一個(gè)二進(jìn)制數(shù)001
,對應(yīng)的十進(jìn)制數(shù),即1
。
console.log(4 & 1)
結(jié)果為0
。知道4
的最后一位是0
,而0 & 1
將是0
。如果你很難理解這一點(diǎn),我們可以使用遞歸函數(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); // true
console.log('prop1' in o); // false
hasOwnProperty
方法,hasOwnProperty()
方法會返回一個(gè)布爾值,指示對象自身屬性中是否具有指定的屬性(也就是,是否有指定的鍵)。console.log(o.hasOwnProperty('prop2')); // true
console.log(o.hasOwnProperty('prop1')); // false
obj['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é)一個(gè)對象。一個(gè)被凍結(jié)的對象再也不能被修改;凍結(jié)了一個(gè)對象則不能向這個(gè)對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結(jié)一個(gè)對象后該對象的原型也不能被修改。freeze()
返回和傳入的參數(shù)相同的對象。ES5新增。
對象不可能擴(kuò)展,也就是不能再添加新的屬性或者方法。
對象已有屬性不允許被刪除。
對象屬性特性不可以重新配置。
Object.seal
方法生成的密封對象,如果屬性是可寫的,那么可以修改屬性值。
Object.freeze
方法生成的凍結(jié)對象,屬性都是不可寫的,也就是屬性值無法更改。
in
運(yùn)算符和 Object.hasOwnProperty
方法有什么區(qū)別?hasOwnPropert()
方法返回值是一個(gè)布爾值,指示對象自身屬性中是否具有指定的屬性,因此這個(gè)方法會忽略掉那些從原型鏈上繼承到的屬性。Object.prototype.phone= '15345025546';
let obj = {
name: '西門大官人',
age: '28'
}
console.log(obj.hasOwnProperty('phone')) // false
console.log(obj.hasOwnProperty('name')) // true
phone
,hasOwnProperty
方法會直接忽略掉。in
運(yùn)算符返回true
。console.log('phone' in obj) // true
in
運(yùn)算符會檢查它或者其原型鏈?zhǔn)欠癜哂兄付Q的屬性。回調(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)用不會,因?yàn)?/span>hoistedFunc
會被提升到作用域的頂部,而notHoistedFunc
不會。作為函數(shù)調(diào)用——如果一個(gè)函數(shù)沒有作為方法、構(gòu)造函數(shù)、apply
、call
調(diào)用時(shí),此時(shí) this
指向的是 window
對象(非嚴(yán)格模式)
//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)用——如果一個(gè)對象的屬性有一個(gè)函數(shù)的值,我們就稱它為方法。調(diào)用該方法時(shí),該方法的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ù)里面會默認(rèn)創(chuàng)建一個(gè)空對象,并將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)用——如果我們想顯式地指定一個(gè)函數(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 對象將是 obj1
reduceAdd.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
,因?yàn)檫@是自 JS 誕生以來null
的實(shí)現(xiàn)。曾經(jīng)有人提出將typeof null == 'object'
修改為typeof null == 'null'
,但是被拒絕了,因?yàn)檫@將導(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
|