前端面試之JS

0.es6新特性

1.不一樣的變量聲明:const和let

2.模板字符串,字符串拼接

3.箭頭函數(Arrow Functions)

4.函數的參數默認值

// ES6之前,當未傳入參數時,text = 'default';
function printText(text) {
    text = text || 'default';
    console.log(text);
}

// ES6;
function printText(text = 'default') {
    console.log(text);
}

printText('hello'); // hello
printText();// default

5.Spread / Rest 操作符

Spread / Rest 操作符指的是    ...   ,具體是 Spread 還是 Rest 需要看上下文語境。 

當被用於迭代器中時,它是一個 Spread 操作符:

function foo(x,y,z) {
  console.log(x,y,z);
}
 
let arr = [1,2,3];
foo(...arr); // 1 2 3

當被用於函數傳參時,是一個 Rest 操作符:當被用於函數傳參時,是一個 Rest 操作符:

function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

6.ES6中的類 

class Student {
  constructor() {
    console.log("I'm a student.");
  }
 
  study() {
    console.log('study!');
  }
 
  static read() {
    console.log("Reading Now.");
  }
}
 
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

 類相當於實例的原型, 所有在類中定義的方法, 都會被實例繼承。 如果在一個方法前, 加上static關鍵字, 就表示該方法不會被實例繼承, 而是直接通過類來調用, 這就稱爲“ 靜態方法”。

 

class Foo {
  static classMethod() {
    return 'hello';
  }
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

類中的繼承和超集:

class Phone {
  constructor() {
    console.log("I'm a phone.");
  }
}
 
class MI extends Phone {
  constructor() {
    super();
    console.log("I'm a phone designed by xiaomi");
  }
}
 
let mi8 = new MI();

 

extends 允許一個子類繼承父類,需要注意的是,子類的constructor 函數中需要執行 super() 函數。
當然,你也可以在子類方法中調用父類的方法,如super.parentMethodName()。
在 這裏 閱讀更多關於類的介紹。

有幾點值得注意的是:

  • 類的聲明不會提升(hoisting),如果你要使用某個 Class,那你必須在使用之前定義它,否則會拋出一個 ReferenceError 的錯誤
  • 在類中定義函數不需要使用 function 關鍵詞

 

1.原型,和原型鏈

javascript語言是一種面向對象的語言,它沒有"子類"和"父類"的概念,裏面所有的數據類型都是對象

             每一個對象都有原型,原型也是一個對象,這個對象也有自己的原型,一層套一層,就是原型鏈。

2.主流瀏覽器5個及其內核

IE                        Trident     微軟

Chrome               Blink      Google

FireFox               Gecko

Opera                   presto

Safari                   webkit   Apple

3.var和let的區別

1.使用var聲明的變量,其作用域爲該語句所在的函數內,且存在變量提升現象;
2.使用let聲明的變量,其作用域爲該語句所在的代碼塊內,不存在變量提升;
3.let不允許在相同作用域內,重複聲明同一個變量。

4.數組Array的方法

  1. concat:合併數組
  2. constructor:數組的 的構造函數
  3. copyWithin:ƒ複製數組的一部分到同一數組中的另一個位置
  4. entries:返回一個新的Array Iterator對象,該對象包含數組中每個索引的鍵/值對。
  5. every:方法用於檢測數組所有元素是否都符合指定條件(通過函數提供)。[1,2,3].every(i=>i>2;)
  6. fill:使用固定值填充數組:
  7. filter:方法創建一個新的數組,新數組中的元素是通過檢查指定數組中符合條件的所有元素。
  8. find:方法返回通過測試(函數內判斷)的數組的第一個元素的值。
  9. findIndex:函數也是查找目標元素,找到就返回元素的位置,找不到就返回-1。
  10. forEach:方法用於調用數組的每個元素,並將元素傳遞給回調函數。
  11. includes:方法用來判斷一個數組是否包含一個指定的值,如果是返回 true,否則false。
  12. indexOf:方法可返回數組中某個指定值的元素位置。
  13. join:方法用於把數組中的所有元素放入一個字符串。元素是通過指定的分隔符進行分隔的。
  14. keys:ƒ keys()
  15. lastIndexOf:方法可返回一個指定的元素在數組中最後出現的位置,從該字符串的後面向前查找。
  16. length:數組長度
  17. map:方法返回一個新數組,數組中的元素爲原始數組元素調用函數處理後的值
  18. pop:最後一個元素出列
  19. push:對未加入一個元素
  20. reduce:
  21. var  arr = [1, 2, 3, 4];
    var sum = arr.reduce((x,y)=>x+y)
    var mul = arr.reduce((x,y)=>x*y)
    console.log( sum ); //求和,10
    console.log( mul ); //求乘積,24
  22. reduceRight:同reduce,從數組末未開始
  23. reverse:反轉數組排列
  24. shift:數組第一個元素出列
  25. slice:方法可從已有的數組中返回選定的元素。
  26. some:方法用於檢測數組中的元素是否滿足指定條件(函數提供)。
  27. sort:排序
  28. splice:方法可刪除從 index 處開始的零個或多個元素,並且用參數列表中聲明的一個或多個值來替換那些被刪除的元素。
  29. toLocaleString:把數組轉換爲本地字符串。
  30. toString:
  31. unshift:方法可向數組的開頭添加一個或更多元素,並返回新的長度。
  32. values:ƒ values()

5.promise

使用promise是爲了解決回調地獄的問題,

firstAsync(function(data){
    //處理得到的 data 數據
    //....
    secondAsync(function(data2){
        //處理得到的 data2 數據
        //....
        thirdAsync(function(data3){
              //處理得到的 data3 數據
              //....
        });
    });
});

回調層層嵌套不易閱讀。 

Promise,他是一個對象,是用來處理異步操作的,可以讓我們寫異步調用的時候寫起來更加優雅,更加美觀便於閱讀。顧名思義爲承諾、許諾的意思,意思是使用了Promise之後他肯定會給我們答覆,無論成功或者失敗都會給我們一個答覆,所以我們就不用擔心他跑了哈哈。所以,Promise有三種狀態:pending(進行中),resolved(完成),rejected(失敗)。只有異步返回的結構可以改變其狀態。所以,promise的過程一般只有兩種:pending->resolved或者pending->rejected。

function cook(){
    console.log('開始做飯。');
    var p = new Promise(function(resolve, reject){        //做一些異步操作
        setTimeout(function(){
            console.log('做飯完畢!');
            reject('飯做糊了')
            // resolve('雞蛋炒飯');
           //一前一後遇到reject,resolve,後面不在執行resolve()
        }, 1000);
    });
    return p;
}
//喫飯
function eat(data){
    console.log('開始喫飯:' + data);
}
function notEat(data) {
    console.log(data)
}


cook()
.then(eat,notEat)

promise對象的構造函數的參數是一個函數function(resolve, reject) ,這個函數有兩個參數resolve,reject,都是函數,他們負責把promise對象的狀態從pending變成resolved,或者rejected。promise對象p的then方法接受兩個函數,第一個在promise狀態爲resolved時執行,第二個在promise狀態爲rejected時執行。resolve的參數會傳遞給對應的函數,rejected同理。eat,notEat,函數可以繼續返回promise對象,然後繼續鏈式調用。

參考資料:https://www.cnblogs.com/sweeeper/p/8442613.html

6.call , apply,arguments

var person = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
}
var person1 = {
    firstName:"Bill",
    lastName: "Gates",
}
var person2 = {
    firstName:"Steve",
    lastName: "Jobs",
}
person.fullName.call(person1);  // 將返回 "Bill Gates"
person.fullName.call(person1);
相當於
var person1=new Person({firstName:"Bill",lastName: "Gates",});
person1.fullName();
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
var str=person.fullName.call(person1, "Seattle", "USA");
console.log(str)     //Bill Gates,Seatle,USA

apply

/*定義一個人類*/
	function Person(name,age)
	{
		this.name=name;
		this.age=age;
	}
	/*定義一個學生類*/
	functionStudent(name,age,grade)
	{
		Person.apply(this,arguments);
		this.grade=grade;
	}
	//創建一個學生類
	var student=new Student("zhangsan",21,"一年級");
	//測試
	alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
	//大家可以看到測試結果name:zhangsan age:21  grade:一年級
	//學生類裏面我沒有給name和age屬性賦值啊,爲什麼又存在這兩個屬性的值呢,這個就是apply的神奇之處.

分析: Person.apply(this,arguments);

this:在創建對象在這個時候代表的是student

arguments:是一個數組,也就是[“zhangsan”,”21”,”一年級”];

                   也就是通俗一點講就是:用student去執行Person這個類裏面的內容,在Person這個類裏面存在this.name等之類的語句,這樣就將屬性創建到了student對象裏面.

相當於python的裝飾器函數,有裝飾器的函數調用之前,先把該函數和其參數放入裝飾器做一定的處理。

@decorateFunc

def  func():

……

Arguments

    function a() {
        console.log(arguments)
        for(let i=0;i<arguments.length;i++){
            console.log(arguments[i])
        }
        console.log(arguments.callee)
    }
    a(1,2,3,4,5,6)

 callee,打印自己函數的代碼……

 function b(q,w,e) {
        e=2
        console.log(arguments[2])
    }

 b(1,1,1)    //打印2
function b(q,w,e) {
        arguments[2]=3
        console.log(e)
    }
b(1,1,1)    //打印3

 函數的參數列表,與arguments列表相互影響,相當於是引用關係。

7.淺拷貝和深拷貝的理解,實現深拷貝

參開資料:https://blog.csdn.net/weixin_37719279/article/details/81240658

淺拷貝只拷貝一層,深拷貝,拷貝每一層

深拷貝,遞歸拷貝,只要array,object,往裏找。

但是當遇到兩個互相引用的對象,會出現死循環的情況。

爲了避免相互引用的對象導致死循環的情況,則應該在遍歷的時候判斷是否相互引用對象,如果是則退出循環。

 

8.dom事件流

參考資料:https://blog.csdn.net/qr457535344/article/details/79711469

流的概念,在現今的JavaScript中隨處可見。比如說React中的單向數據流,Node中的流,又或是今天本文所講的DOM事件流。都是流的一種生動體現。
至於流的具體概念,我們採用下文的解釋:

用術語說流是對輸入輸出設備的抽象。以程序的角度說,流是具有方向的數據。
通通連起來——無處不在的流 淘寶FED--愈之

事件流之事件冒泡與事件捕獲

在瀏覽器發展的過程中,開發團隊遇到了一個問題。那就是頁面中的哪一部分擁有特定的事件
可以想象畫在一張紙上的一組同心圓,如果你把手指放在圓心上,那麼你的手指指向的其實不是一個圓,而是紙上所有的圓。放到實際頁面中就是,你點擊一個按鈕,事實上你還同時點擊了按鈕所有的父元素
開發團隊的問題就在於,當點擊按鈕時,是按鈕最外層的父元素先收到事件並執行,還是具體元素先收到事件並執行?所以這兒引入了事件流的概念。

事件流所描述的就是從頁面中接受事件的順序。

 

因爲有兩種觀點,所以事件流也有兩種,分別是事件冒泡和事件捕獲。現行的主流是事件冒泡。

事件冒泡

事件冒泡即事件開始時,由最具體的元素接收(也就是事件發生所在的節點),然後逐級傳播到較爲不具體的節點。
舉個栗子,就很容易明白了。 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Event Bubbling</title>
</head>
<body>
  <button id="clickMe">Click Me</button>
</body>
</html>

然後,我們給button和它的父元素,加入點擊事件。

var button = document.getElementById('clickMe');
 
button.onclick = function() {
  console.log('1. You click Button');
};
document.body.onclick = function() {
  console.log('2. You click body');
};
document.onclick = function() {
  console.log('3. You click document');
};
window.onclick = function() {
  console.log('4. You click window');
};

 

在代碼所示的頁面中,如果點擊了button,那麼這個點擊事件會按如下的順序傳播(Chrome瀏覽器):

  1. button

  2. body

  3. document

  4. window

也就是說,click事件首先在<button>元素上發生,然後逐級向上傳播。這就是事件冒泡。

 

事件捕獲

事件捕獲的概念,與事件冒泡正好相反。它認爲當某個事件發生時,父元素應該更早接收到事件,具體元素則最後接收到事件。比如說剛纔的demo,如果是事件捕獲的話,事件發生順序會是這樣的:

  1. window

  2. document

  3. body

  4. button


當然,由於時代更迭,事件冒泡方式更勝一籌。所以放心的使用事件冒泡,有特殊需要再使用事件捕獲即可。

(1)捕獲階段:當我們在 DOM 樹的某個節點發生了一些操作(例如單擊、鼠標移動上去),就會有一個事件發射過去。這個事件從 Window 發出,不斷經過下級節點直到觸發的目標節點。在到達目標節點之前的過程,就是捕獲階段(Capture Phase)。(所有經過的節點,都會觸發這個事件。捕獲階段的任務就是建立這個事件傳遞路線,以便後面冒泡階段順着這條路線返回 Window。)在目標元素對象本身上註冊的捕獲事件處理程序不會被調用。

(2)目標階段:當事件不斷的傳遞直到目標節點的時候,最終在目標節點上觸發這個事件,就是目標階段。

(3)冒泡階段:事件冒泡即事件開始時,由最具體的元素接收(也就是事件發生所在的節點),然後逐級傳播到較爲不具體的節點(我們平時用的事件綁定就是利用的事件冒泡的原理)

事件冒泡過程,是可以被阻止的。防止事件冒泡而帶來不必要的錯誤和困擾。

這個方法就是:stopPropagation()

stopPropagation() 方法:終止事件在傳播過程的捕獲、目標處理或起泡階段進一步傳播。調用該方法後,該節點上處理該事件的處理程序將被調用,事件不再被分派到其他節點

 

9.前端項目管理

- src/
  - main/                    # main 目錄
    - alpha/                 # alpha 頁面
      - index.css            # css 入口文件
      - index.js             # js 入口文件
      - index.html           # html 入口文件
      - ...
    - beta/                  # beta 頁面
      - index.css
      - index.js
      - index.html
      - ...
    - ...
目錄結構按頁面分,不要按文件類型分 

  • 可擴展性:能夠很方便、清晰的擴展一個頁面、組件、模塊
  • 組件化:多個頁面之間共用的大塊代碼可以獨立成組件,多個頁面、組件之間共用的小塊代碼可以獨立成公共模塊
  • 可閱讀性:閱讀性良好(包括目錄文件結構、代碼結構),能夠很快捷的找到某個頁面、組件的文件,也能快捷的看出項目有哪些頁面、組件
  • 可移植性:能夠輕鬆的對項目架構進行升級,或移植某些頁面、組件、模塊到其他項目
  • 可重構性:對某個頁面、組件、模塊進行重構時,能夠保證在重構之後功能不會改變、不會產生新 bug
  • 開發友好:開發者在開發某一個功能時,能夠有比較好的體驗(不好的體驗比如:多個文件相隔很遠
  • 協作性:多人協作時,很少產生代碼衝突、文件覆蓋等問題
  • 可交接性:當有人要離開項目時,交接給其他人是很方便的

更多內容:https://blog.csdn.net/cc9200/article/details/101839894

10.跨域的理解和解決

首先,我們得先理解一下何爲跨域?所謂跨域,即網站的協議名 protocol(例如 http ://) 、域名 host (例如:www.example.com)、端口號 port (例如 80 ,默認端口可以省略) 這三個中的任意一個不同,網站之間的數據傳輸或者請求就屬於跨域請求了。

這是由於瀏覽器的同源策略,爲了防範跨站腳本的攻擊,禁止客戶端腳本對不同域的服務進行跨站調用,但是跨域並非瀏覽器限制了發起跨站請求,而是跨站請求可以正常發起,但返回結果被瀏覽器攔截了。有些瀏覽器不允許從HTTPS協議的域 跨域訪問 HTTP協議,比如Chrome和Firefox,這些瀏覽器在請求還未發出的時候就會攔截請求,這是一個特例。如果是非同源,共有三種行爲受到限制:(1)cookie、LocalStorage 和 IndexDB 無法讀取;(2)DOM 無法獲得;(3)AJAX請求不能發送。

1、利用 JSONP 實現跨域

$.getJSON('http://jjjjjjjj.com/data?callback=?,function(data)'){
    //處理獲得的json數據
});

2、利用 CORS 實現跨域 

CORS (Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問跨域資源時,瀏覽器與服務器應該如何溝通。CORS 背後的思想是使用自定義的 HTTP 頭部,讓服務器能聲明哪些來源可以通過瀏覽器訪問該服務器上的資源,從而決定請求或響應是應該成功還是失敗,CORS 本身並非絕對的安全,可利用 OAuth2 加強保障。(更多關於 CORS 的詳解可以查看 阮一峯老師的一篇文章:跨域資源共享 CORS 詳解

header("Access-Control-Allow-Origin: *")       //“*”號表示允許任何域向我們的服務端提交請求
header("Access-Control-Allow-Origin: http://jjjjj.jd.com")      //也可以設置指定的域名

11.js數據類型:

undefined、null、boolean、number、string、object、function、array

 12.jquery鏈式調用實現原理

jq的方法都是掛在原型的,那麼如果我們每次在內部方法返回this,也就是返回實例對象,那麼我們就可以繼續調用原型上的方法了,這樣的就節省代碼量,提高代碼的效率,代碼看起來更優雅。

var MyJQ = function(){
       }
        MyJQ.prototype = {
            css:function(){
               console.log("設置css樣式");
                return this;
            },
           show:function(){
                console.log("將元素顯示");
               return this;
            },
           hide:function(){
                console.log("將元素隱藏");
           }
       };
        var myjq = new MyJQ();
        myjq.css().css().show().hide();

 

12.防抖節流

1、什麼是函數防抖

函數防抖(debounce):就是讓某個函數在上一次執行之後,滿足等待某個時間內不再觸發此函數後再執行,而在這個等待時間內再次觸發函數,等待時間可以重新計算,知道該函數在一定間隔內沒有被調用時,纔開始執行被調用方法(所謂防抖,就是指觸發事件後在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。

 

應用場景:假設我們網站有個所搜框,用戶輸入文本我們會自動聯想匹配出一些結果供用戶選擇。

我們可能首先想到到的做法就是監聽keypress事件,然後異步去查詢結果

這個方法本事是沒有錯的,但是如果用戶快速的輸入一連串字符,假設是10個字符,就會在瞬間觸發了10次請求,這無疑不是我們想要的。

什麼是函數節流

函數節流(throttle):是讓一個函數無法在很短的時間間隔內連續調用,當上一次執行完之後過了規定的時間間隔,才能進行下一次的函數調用。(所謂節流,就是指連續觸發事件但是在N秒中只執行一次函數)節流會稀釋函數的執行頻率。

 

13.如何判斷一個變量是數組類型還是對象類型

instanceof

14.for in for of forEach map的區別

 可遍歷對象不同,index或key的值類型不同,只有for in可以遍歷自定義屬性和原型鏈的自定義屬性。

 

15.模塊機制,amd和commonjs

先回答我:爲什麼模塊很重要?

答:因爲有了模塊,我們就可以更方便地使用別人的代碼,想要什麼功能,就加載什麼模塊。
但是,這樣做有一個前提,那就是大家必須以同樣的方式編寫模塊,否則你有你的寫法,我有我的寫法,豈不是亂了套!

於是下面三個模塊規範出來了,這篇文章也出來了

16.this對象和bind,call函數使用

func.bind(obj)

obj.func()

var User = {
  count: 1,
  getCount: function() {
    return this.count;
  }
};

console.log(User.getCount());
var func = User.getCount;
console.log(func());
// 1和undefined。

var func = User.getCount.bind(User);
console.log(func());

17.proptype原型對象的理解,實現一個方法,傳入一個string類型的參數,然後將string的每個字符間加個空格返回

spacify('hello world') // => 'h e l l o  w o r l d'
'hello world'.spacify();
String.prototype.spacify = function(){
  return this.split('').join(' ');
};

18.函數申明與函數表達式的區別

// 函數聲明
    function funDeclaration(type){
        return type==="Declaration";
    }
// 函數表達式
    var funExpression = function(type){
        return type==="Expression";
    }

 

用函數聲明創建的函數funDeclaration可以在funDeclaration定義之前就進行調用;而用函數表達式創建的funExpression函數不能在funExpression被賦值之前進行調用。

19.函數式編程,實現函數multi(2)(3)(4)=24

  function multi(num) {
        var func=function (x){
           return multi(x*num)
        }
        func.valueOf=function(){
            return num;
        }
        return func
    }
   
    console.log(cmulti(1))               // 1
    console.log(cmulti(1)(2)(3)(4))      // 24
    console.log(cmulti(1)(2)(3)(4)(5)(6))// 720

valueOf()方法和toString()方法是一樣的,都會在後臺進行隱式的調用,在 o = +o時,等號右邊就已經調用了valueOf(),相當於 o = +(-1);所以,最終結果o爲-1.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章