首頁>技術>

40. ECMAScript 是什麼?

ECMAScript 是編寫指令碼語言的標準,這意味著JavaScript遵循ECMAScript標準中的規範變化,因為它是JavaScript的藍圖。

ECMAScript 和 Javascript,本質上都跟一門語言有關,一個是語言本身的名字,一個是語言的約束條件只不過發明JavaScript的那個人(Netscape公司),把東西交給了ECMA(European Computer Manufacturers Association),這個人規定一下他的標準,因為當時有java語言了,又想強調這個東西是讓ECMA這個人定的規則,所以就這樣一個神奇的東西誕生了,這個東西的名稱就叫做ECMAScript。

javaScript = ECMAScript + DOM + BOM(自認為是一種廣義的JavaScript)

ECMAScript說什麼JavaScript就得做什麼!

JavaScript(狹義的JavaScript)做什麼都要問問ECMAScript我能不能這樣幹!如果不能我就錯了!能我就是對的!

——突然感覺JavaScript好沒有尊嚴,為啥要搞個人出來約束自己,

那個人被創造出來也好委屈,自己被創造出來完全是因為要約束JavaScript。

41. ES6或ECMAScript 2015有哪些新特性?箭頭函式類模板字串加強的物件字面量物件解構Promise生成器模組Symbol代理Set函式預設引數rest 和展開塊作用域42. var,let和const的區別是什麼?

var宣告的變數會掛載在window上,而let和const宣告的變數不會:

var a = 100;console.log(a,window.a); // 100 100let b = 10;console.log(b,window.b); // 10 undefinedconst c = 1;console.log(c,window.c); // 1 undefined

var宣告變數存在變數提升,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已經被聲明了。

暫存死區

var a = 100;if(1){ a = 10; //在當前塊作用域中存在a使用let/const宣告的情況下,給a賦值10時,只會在當前作用域找變數a, // 而這時,還未到宣告時候,所以控制檯Error:a is not defined let a = 1;}

const

/** 1、一旦宣告必須賦值,不能使用null佔位。** 2、聲明後不能再修改** 3、如果宣告的是複合型別資料,可以修改其屬性** */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'}
43. 什麼是箭頭函式?

箭頭函式表示式的語法比函式表示式更簡潔,並且沒有自己的this,arguments,super或new.target。箭頭函式表示式更適用於那些本來需要匿名函式的地方,並且它不能用作建構函式。

//ES5 Versionvar getCurrentDate = function (){ return new Date();}//ES6 Versionconst getCurrentDate = () => new Date();

在本例中,ES5 版本中有function(){}宣告和return關鍵字,這兩個關鍵字分別是建立函式和返回值所需要的。在箭頭函式版本中,我們只需要()括號,不需要 return 語句,因為如果我們只有一個表示式或值需要返回,箭頭函式就會有一個隱式的返回。

//ES5 Versionfunction greet(name) { return 'Hello ' + name + '!';}//ES6 Versionconst greet = (name) => `Hello ${name}`;const greet2 = name => `Hello ${name}`;

我們還可以在箭頭函式中使用與函式表示式和函式宣告相同的引數。如果我們在一個箭頭函式中有一個引數,則可以省略括號。

const getArgs = () => argumentsconst getArgs2 = (...rest) => rest

箭頭函式不能訪問arguments物件。所以呼叫第一個getArgs函式會丟擲一個錯誤。相反,我們可以使用rest引數來獲得在箭頭函式中傳遞的所有引數。

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值。它捕獲詞法作用域函式的this值,在此示例中,addAll函式將複製computeResult 方法中的this值,如果我們在全域性作用域宣告箭頭函式,則this值為 window 物件。

44. 什麼是類?

類(class)是在 JS 中編寫建構函式的新方法。它是使用建構函式的語法糖,在底層中使用仍然是原型和基於原型的繼承。

 //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" 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); // trueconsole.log(s.toString === Object.prototype.toString); // true
45. 什麼是模板字串?

模板字串是在 JS 中建立字串的一種新方法。我們可以通過使用反引號使模板字串化。

//ES5 Versionvar greet = 'Hi I\\'m Mark';//ES6 Versionlet greet = `Hi I'm Mark`;

在 ES5 中我們需要使用一些轉義字元來達到多行的效果,在模板字串不需要這麼麻煩:

//ES5 Versionvar lastWords = '\\n' + ' I \\n' + ' Am \\n' + 'Iron Man \\n';//ES6 Versionlet lastWords = ` I Am Iron Man`;

在ES5版本中,我們需要新增\\n以在字串中新增新行。在模板字串中,我們不需要這樣做。

//ES5 Versionfunction greet(name) { return 'Hello ' + name + '!';}//ES6 Versionfunction greet(name) { return `Hello ${name} !`;}

在 ES5 版本中,如果需要在字串中新增表示式或值,則需要使用+運算子。在模板字串s中,我們可以使用${expr}嵌入一個表示式,這使其比 ES5 版本更整潔。

46. 什麼是物件解構?

物件析構是從物件或陣列中獲取或提取值的一種新的、更簡潔的方法。假設有如下的物件:

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;
47. 什麼是 ES6 模組?

模組使我們能夠將程式碼基礎分割成多個檔案,以獲得更高的可維護性,並且避免將所有程式碼放在一個大檔案中。在 ES6 支援模組之前,有兩個流行的模組。

CommonJS-Node.jsAMD(非同步模組定義)-瀏覽器

基本上,使用模組的方式很簡單,import用於從另一個檔案中獲取功能或幾個功能或值,同時export用於從檔案中公開功能或幾個功能或值。

匯出

使用 ES5 (CommonJS)

// 使用 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 模組

// 使用 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)

// 使用 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

// 使用 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)

// 使用 ES5 (CommonJS) - index.jsconst Helpers = require('./helpers.js');console.log(Helpers.isNull(null));

使用 ES6 Modules

import Helpers from '.helpers.js'console.log(Helpers.isNull(null));
48. 什麼是Set物件,它是如何工作的?

Set 物件允許你儲存任何型別的唯一值,無論是原始值或者是物件引用。

我們可以使用Set建構函式建立Set例項。

const set1 = new Set();const set2 = new Set(["a","b","c","d","d","e"]);

我們可以使用add方法向Set例項中新增一個新值,因為add方法返回Set物件,所以我們可以以鏈式的方式再次使用add。如果一個值已經存在於Set物件中,那麼它將不再被新增。

set2.add("f");set2.add("g").add("h").add("i").add("j").add("k").add("k");// 後一個“k”不會被新增到set物件中,因為它已經存在了

我們可以使用has方法檢查Set例項中是否存在特定的值。

set2.has("a") // trueset2.has("z") // true

我們可以使用size屬性獲得Set例項的長度。

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]
49. 什麼是回撥函式?

回撥函式是一段可執行的程式碼段,它作為一個引數傳遞給其他的程式碼,其作用是在需要的時候方便呼叫這段(回撥函式)程式碼。

在JavaScript中函式也是物件的一種,同樣物件可以作為引數傳遞給函式,因此函式也可以作為引數傳遞給另外一個函式,這個作為引數的函式就是回撥函式。

const btnAdd = document.getElementById('btnAdd');btnAdd.addEventListener('click', function clickCallback(e) { // do something useless});

在本例中,我們等待id為btnAdd的元素中的click事件,如果它被單擊,則執行clickCallback函式。回撥函式向某些資料或事件新增一些功能。

陣列中的reduce、filter和map方法需要一個回撥作為引數。回撥的一個很好的類比是,當你打電話給某人,如果他們不接,你留下一條訊息,你期待他們回撥。呼叫某人或留下訊息的行為是事件或資料,回撥是你希望稍後發生的操作。

50. Promise 是什麼?

Promise 是非同步程式設計的一種解決方案:從語法上講,promise是一個物件,從它可以獲取非同步操作的訊息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。promise有三種狀態:pending(等待態),fulfiled(成功態),rejected(失敗態);狀態一旦改變,就不會再變。創造promise例項後,它會立即執行。

fs.readFile('somefile.txt', function (e, data) { if (e) { console.log(e); } console.log(data);});

如果我們在回撥內部有另一個非同步操作,則此方法存在問題。我們將有一個混亂且不可讀的程式碼。此程式碼稱為“回撥地獄”。

// 回撥地獄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有三種不同的狀態:

pending:初始狀態,完成或失敗狀態的前一個狀態fulfilled:操作成功完成rejected:操作失敗

pending 狀態的 Promise 物件會觸發 fulfilled/rejected 狀態,在其狀態處理方法中可以傳入引數/失敗資訊。當操作成功完成時,Promise 物件的 then 方法就會被呼叫;否則就會觸發 catch。如:

const myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("成功!"); }, 250);});myFirstPromise.then((data) => { console.log("Yay! " + data);}).catch((e) => {...});
51. 什麼是 async/await 及其如何工作?

async/await是 JS 中編寫非同步或非阻塞程式碼的新方法。它建立在Promises之上,讓非同步程式碼的可讀性和簡潔度都更高。

async/await是 JS 中編寫非同步或非阻塞程式碼的新方法。它建立在Promises之上,相對於 Promise 和回撥,它的可讀性和簡潔度都更高。但是,在使用此功能之前,我們必須先學習Promises的基礎知識,因為正如我之前所說,它是基於Promise構建的,這意味著幕後使用仍然是Promise。

使用 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

在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關鍵宣告函式會隱式返回一個Promise。

const giveMeOne = async () => 1;giveMeOne() .then((num) => { console.log(num); // logs 1 });

注意:await關鍵字只能在async function中使用。在任何非async function的函式中使用await關鍵字都會丟擲錯誤。await關鍵字在執行下一行程式碼之前等待右側表示式(可能是一個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 functionasync function getTwo() { try { const num1 = await giveMeOne(); // 這行會等待右側表示式執行完成 const num2 = await giveMeOne(); return num1 + num2; } catch (e) { console.log(e); }}await getTwo(); // 2
52. 展開(spread )運算子和 剩餘(Rest) 運算子有什麼區別?

展開運算子(spread)是三個點(...),可以將一個數組轉為用逗號分隔的引數序列。說的通俗易懂點,有點像化骨綿掌,把一個大元素給打散成一個個單獨的小元素。

剩餘運算子也是用三個點(...)表示,它的樣子看起來和展開操作符一樣,但是它是用於解構陣列和物件。在某種程度上,剩餘元素和展開元素相反,展開元素會“展開”陣列變成多個元素,剩餘元素會收集多個元素和“壓縮”成一個單一的元素。

function add(a, b) { return a + b;};const nums = [5, 6];const sum = add(...nums);console.log(sum);

在本例中,我們在呼叫add函式時使用了展開操作符,對nums陣列進行展開。所以引數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)); // 15

在本例中,我們有一個add函式,它接受任意數量的引數,並將它們全部相加,然後返回總數。

const [first, ...others] = [1, 2, 3, 4, 5];console.log(first); // 1console.log(others); // [2,3,4,5]

這裡,我們使用剩餘操作符提取所有剩餘的陣列值,並將它們放入除第一項之外的其他陣列中。

53. 什麼是預設引數?

預設引數是在 JS 中定義預設變數的一種新方法,它在ES6或ECMAScript 2015版本中可用。

//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 1

我們還可以在預設引數中使用解構。

function 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"
54. 什麼是包裝物件(wrapper object)?

我們現在複習一下JS的資料型別,JS資料型別被分為兩大類,基本型別和引用型別。

基本型別: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,屬於基本型別,所以它沒有屬性和方法,但是在這個例子中,我們呼叫了一個toUpperCase()方法,它不會丟擲錯誤,還返回了物件的變數值。

原因是基本型別的值被臨時轉換或強制轉換為物件,因此name變數的行為類似於物件。除null和undefined之外的每個基本型別都有自己包裝物件。也就是:String,Number,Boolean,Symbol和BigInt。在這種情況下,name.toUpperCase()在幕後看起來如下:

console.log(new String(name).toUpperCase()); // "MARKO"

在完成訪問屬性或呼叫方法之後,新建立的物件將立即被丟棄。

55. 隱式和顯式轉換有什麼區別)?

隱式強制轉換是一種將值轉換為另一種型別的方法,這個過程是自動完成的,無需我們手動操作。

假設我們下面有一個例子。

console.log(1 + '6'); // 16console.log(false + true); // 1console.log(6 * '2'); // 12

第一個console.log語句結果為16。在其他語言中,這會丟擲編譯時錯誤,但在 JS 中,1被轉換成字串,然後與+運算子連線。我們沒有做任何事情,它是由 JS 自動完成。

第二個console.log語句結果為1,JS 將false轉換為boolean 值為 0,,true為1,因此結果為1。

第三個console.log語句結果12,它將'2'轉換為一個數字,然後乘以6 * 2,結果是12。

而顯式強制是將值轉換為另一種型別的方法,我們需要手動轉換。

console.log(1 + parseInt('6'));

在本例中,我們使用parseInt函式將'6'轉換為number ,然後使用+運算子將1和6相加。

56. 什麼是NaN?以及如何檢查值是否為NaN?

NaN表示“非數字”是 JS 中的一個值,該值是將數字轉換或執行為非數字值的運算結果,因此結果為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

JS 有一個內建的isNaN方法,用於測試值是否為isNaN值,但是這個函式有一個奇怪的行為。

console.log(isNaN()); // trueconsole.log(isNaN(undefined)); // trueconsole.log(isNaN({})); // trueconsole.log(isNaN(String('a'))); // trueconsole.log(isNaN(() => { })); // true

所有這些console.log語句都返回true,即使我們傳遞的值不是NaN。

在ES6中,建議使用Number.isNaN方法,因為它確實會檢查該值(如果確實是NaN),或者我們可以使自己的輔助函式檢查此問題,因為在 JS 中,NaN是唯一的值,它不等於自己。

function checkIfNaN(value) { return value !== value;}
57. 如何判斷值是否為陣列?

我們可以使用Array.isArray方法來檢查值是否為陣列。當傳遞給它的引數是陣列時,它返回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 })); // falseconsole.log(Array.isArray([])); // true

如果環境不支援此方法,則可以使用polyfill實現。

function isArray(value){ return Object.prototype.toString.call(value) === "[object Array]"}

當然還可以使用傳統的方法:

let a = []if (a instanceof Array) { console.log('是陣列')} else { console.log('非陣列')}
58. 如何在不使用%模運算子的情況下檢查一個數字是否是偶數?

我們可以對這個問題使用按位&運算子,&對其運算元進行運算,並將其視為二進位制值,然後執行與運算。

function isEven(num) { if (num & 1) { return false } else { return true }}

0 二進位制數是 0001 二進位制數是 0012 二進位制數是 0103 二進位制數是 0114 二進位制數是 1005 二進位制數是 1016 二進位制數是 1107 二進位制數是 111

以此類推...

與運算的規則如下:

aba & b000010111

因此,當我們執行console.log(5&1)這個表示式時,結果為1。首先,&運算子將兩個數字都轉換為二進位制,因此5變為101,1變為001。

然後,它使用按位懷運算子比較每個位(0和1)。 101&001,從表中可以看出,如果a & b為1,所以5&1結果為1。

101 & 001101001001

首先我們比較最左邊的1&0,結果是0。然後我們比較中間的0&0,結果是0。然後我們比較最後1&1,結果是1。最後,得到一個二進位制數001,對應的十進位制數,即1。

由此我們也可以算出console.log(4 & 1) 結果為0。知道4的最後一位是0,而0 & 1 將是0。如果你很難理解這一點,我們可以使用遞迴函式來解決此問題。

function isEven(num) {if (num < 0 || num === 1) return false;if (num == 0) return true;return isEven(num - 2);}
59. 如何檢查物件中是否存在某個屬性?

檢查物件中是否存在屬性有三種方法。

第一種使用 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")); // false

第三種使用括號符號obj["prop"]。如果屬性存在,它將返回該屬性的值,否則將返回undefined。

console.log(o["prop"]); // "bwahahah"console.log(o["prop1"]); // undefined
60. AJAX 是什麼?

即非同步的 JavaScript 和 XML,是一種用於建立快速動態網頁的技術,傳統的網頁(不使用 AJAX)如果需要更新內容,必需過載整個網頁面。使用AJAX則不需要載入更新整個網頁,實現部分內容更新

用到AJAX的技術:

HTML - 網頁結構CSS - 網頁的樣式JavaScript - 操作網頁的行為和更新DOMXMLHttpRequest API - 用於從伺服器傳送和獲取資料PHP,Python,Nodejs - 某些伺服器端語言61. 如何在 JS 中建立物件?

使用物件字面量:

 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"

使用 Object.create 方法:

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"
62. Object.seal 和 Object.freeze 方法之間有什麼區別?

這兩種方法之間的區別在於,當我們對一個物件使用Object.freeze方法時,該物件的屬性是不可變的,這意味著我們不能更改或編輯這些屬性的值。而在Obj.Engor方法中,我們可以改變現有的屬性。

Object.freeze()

Object.freeze() 方法可以凍結一個物件。一個被凍結的物件再也不能被修改;凍結了一個物件則不能向這個物件新增新的屬性,不能刪除已有屬性,不能修改該物件已有屬性的可列舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個物件後該物件的原型也不能被修改。freeze() 返回和傳入的引數相同的物件。

Object.seal()

Object.seal()方法封閉一個物件,阻止新增新屬性並將所有現有屬性標記為不可配置。當前屬性的值只要可寫就可以改變。

方法的相同點:

ES5新增。物件不可能擴充套件,也就是不能再新增新的屬性或者方法。物件已有屬性不允許被刪除。物件屬性特性不可以重新配置。

方法不同點:

Object.seal方法生成的密封物件,如果屬性是可寫的,那麼可以修改屬性值。Object.freeze方法生成的凍結物件,屬性都是不可寫的,也就是屬性值無法更改。63. in 運算子和 Object.hasOwnProperty 方法有什麼區別?

如你所知,這兩個特性都檢查物件中是否存在屬性,它將返回truefalse。它們之間的區別在於,in操作符還會檢查物件的原型鏈,如果屬性在當前物件中沒有找到,而hasOwnProperty方法只檢查屬性是否存在於當前物件中,而忽略原型鏈。

hasOwnPropert方法

hasOwnPropert()方法返回值是一個布林值,指示物件自身屬性中是否具有指定的屬性,因此這個方法會忽略掉那些從原型鏈上繼承到的屬性。

看下面的例子:

Object.prototype.phone= '15345025546';let obj = { name: '西門大官人', age: '28'}console.log(obj.hasOwnProperty('phone')) // falseconsole.log(obj.hasOwnProperty('name')) // true

可以看到,如果在函式原型上定義一個變數phone,hasOwnProperty方法會直接忽略掉。

in 運算子

如果指定的屬性在指定的物件或其原型鏈中,則in 運算子返回true。

還是用上面的例子來演示:

console.log('phone' in obj) // true

可以看到in運算子會檢查它或者其原型鏈是否包含具有指定名稱的屬性。

64. 有哪些方法可以處理 JS 中的非同步程式碼?回撥Promiseasync/await還有一些庫:async.js, bluebird, q, co65. 函式表示式和函式宣告之間有什麼區別?

看下面的例子:

hoistedFunc();notHoistedFunc();function hoistedFunc(){ console.log("注意:我會被提升");}var notHoistedFunc = function(){ console.log("注意:我沒有被提升");}

notHoistedFunc呼叫丟擲異常:Uncaught TypeError: notHoistedFunc is not a function,而hoistedFunc呼叫不會,因為hoistedFunc會被提升到作用域的頂部,而notHoistedFunc 不會。

66. 呼叫函式,可以使用哪些方法?

在 JS 中有4種方法可以呼叫函式。

作為函式呼叫——如果一個函式沒有作為方法、建構函式、apply、call 呼叫時,此時 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" 物件 });
作為方法呼叫——如果一個物件的屬性有一個函式的值,我們就稱它為方法。呼叫該方法時,該方法的this值指向該物件。
const details = { name : "Marko", getName(){ return this.name; }}details.getName(); // Marko// the "this" value inside "getName" method will be the "details" object
作為建構函式的呼叫-如果在函式之前使用new關鍵字呼叫了函式,則該函式稱為建構函式。建構函式裡面會預設建立一個空物件,並將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方法呼叫——如果我們想顯式地指定一個函式的this值,我們可以使用這些方法,這些方法對所有函式都可用。
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 函式中的 this 物件將是 obj1reduceAdd.call(obj2, 1, 2, 3, 4, 5); // reduceAdd 函式中的 this 物件將是 obj2
67. 什麼是快取及它有什麼作用?

快取是建立一個函式的過程,這個函式能夠記住之前計算的結果或值。使用快取函式是為了避免在最後一次使用相同引數的計算中已經執行的函式的計算。這節省了時間,但也有不利的一面,即我們將消耗更多的記憶體來儲存以前的結果。

68. 手動實現快取方法
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);
69. 為什麼typeof null 返回 object?如何檢查一個值是否為 null?

typeof null == 'object'總是返回true,因為這是自 JS 誕生以來null的實現。曾經有人提出將typeof null == 'object'修改為typeof null == 'null',但是被拒絕了,因為這將導致更多的bug。

我們可以使用嚴格相等運算子===來檢查值是否為null。

function isNull(value){ return value === null;}
70. new 關鍵字有什麼作用?

new關鍵字與建構函式一起使用以建立物件在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關鍵字做了4件事:

建立空物件 {}將空物件分配給 this 值將空物件的__proto__指向建構函式的prototype如果沒有使用顯式return語句,則返回this

根據上面描述的,它將首先建立一個空物件{},然後它將this值賦給這個空物件this={},並向這個物件新增屬性。因為我們沒有顯式的return語句,所以它會自動為我們返回this。

英文原文地址:/file/2020/06/16/20200616032820_14740.jpg

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 使用Streamlit從簡單的Python指令碼建立互動式WebApp