js

灵火發表於2024-05-08

JS 有哪些資料型別?

7種資料型別 string number bool undefined null object symbol

object 包括了陣列、函式、正則、日期等物件 一旦出現(陣列、函式、正則、日期、NaN)直接0分

(必考)什麼是立即執行函式?使用立即執行函式的目的是什麼?

宣告一個函式,不給他名字(也可以給)然後在後面加上括號,立即執行他,由於這個語法不對,他不能立即執行,所以修改一下語法,讓他立即執行,比如用括號包起來,前面加一個!,或者~

;(function (){
     var name
 }())
 //為了防止括號和前面的相作用,就新增一個分號,以示分離
 ;(function (){
     var name
 })()
 !!!!!!!function (){
     var name
 }()
 ~function (){
     var name
 }()
複製程式碼

作用: 造出一個函式作用域,防止汙染全域性變數 侃侃而談 ES 6 有一個新語法,可以直接宣告區域性變數,花括號{}就代替了立即執行函式。

 {
     let  name
 }
複製程式碼

JS 如何實現繼承?

舉例子寫出來:(要理解下面每句話的意思)

  1. 原型鏈
function Animal(){
      this.body = '肉體'
  }
  Animal.prototype.move = function(){
  }
複製程式碼

下面寫一個Human繼承animal

  function Human(name){
      Animal.apply(this, arguments)//自身屬性的繼承
      this.name = name
  }
  // Human.prototype.__proto__ = Animal.prototype // 非法,不被支援
	//所以得生成一個空函式,下面這三句話就是上面這句話的意思
  var f = function(){}
  f.prototype = Animal.prototype
  Human.prototype = new f()
//然後再寫Human自己的方法。這個時候我們就說Human繼承了animal
  Human.prototype.useTools = function(){}
複製程式碼

var frank = new Human()

  1. extends 關鍵字
class Animal{
      constructor(){
          this.body = '肉體'
      },
      move(){}//這裡就不實現了
  }
  class Human extends Animal{
      constructor(name){
          super()
          this.name = name
      },
      useTools(){}
  }
  var frank = new Human()
複製程式碼

如果只記住下面這種沒有問題,能記住原型鏈的更好,class語法知識語法糖。

移動端的觸控事件瞭解嗎?

  1. 講一下這幾個事件: touchstart touchmove touchend touchcancel
  2. 模擬 swipe 事件:記錄兩次 touchmove 的位置差,如果後一次在前一次的右邊,說明向右滑了。

JS

&&和||

&&,他的返回值就是遇到的第一個falsy值,後面的不看了 1&&2&&3&&0&&2&&3 返回0,後面的不執行。 ||,他返回的就是第一個遇到的true值,後面的不看 0||0||0||false||1||2||0返回1,後面的不執行。

七個資料型別

七種資料型別分為六個基本型別和物件。 六個基本型別分別是nullundefinedstringnumberbooleansymbol 物件又分為objectfunctionarray

null和undefined的區別

相同點:if裡都是都是false值

不同點:

  1. null轉換為數字的時候是0undefined表示未定義轉換為數字的時候是NaN

  2. 呼叫函式時,某個引數未設定任何值,這時就可以傳入null,表示該引數為空。而傳入undefined就代表undefined

typeof

yuchengkai.cn/docs/zh/fro… typeof只需要記住兩點

  • typeof 對於基本型別,除了 null 顯示為 object,其餘都可以顯示正確的型別
  • typeof 對於物件,除了函式為function都會顯示object
  • typeof運算子只能顯示陣列的型別是Object,而Array.isArray(arr)方法可以識別陣列

六個false值

除了下面六個值被轉為false,其他值都視為true

undefined
null
false
0
NaN
""''(空字串)
複製程式碼

NaN

NaN === NaN;        // false
typeof NaN//number
複製程式碼

for in 和forEach

for in 遍歷物件

for in迴圈注意點

for(var key in person){
    console.log(person.key)
}
複製程式碼

注意:person.key 等於 person['key'] 要用 這裡遍歷要用person[key]

for... in可能會隨機遍歷,不按照宣告順序

forEach遍歷陣列

var arr=[1,2,3]
arr.forEach(function(e){
	console.log(e)
})
//1  2   3

複製程式碼

阿斯蒂

其他型別轉換為String

  • nullundefined沒有tostring()方法。只能null+''undefined+''
  • 其他的用.toString()方法。或直接+''。或者window.String()方法

其他型別轉換為Number(主要是String)

五個方法

在這裡插入圖片描述

  • 簡便方法'1234'-0(常用)
  • .parseInt()//預設十進位制
  • 簡便方法二+'1'取正

其他型別轉換為Boolean(五個false值)

記住5個false值 null undefined NaN 0 ''

記住下面易錯點:

[]//ture
{}//ture
' '//ture 裡面有空格
複製程式碼

轉換方法兩個:

  • window.Boolean(xxx)
  • 前面加上!!xxx

記憶體

  • 資料區(存的變數的資料)分為 Stack(棧記憶體) 和 Heap(堆記憶體)
  • 簡單型別的資料直接存在 Stack 裡
  • 複雜型別的資料是把 Heap 地址存在 Stack 裡(物件包括:狹義物件(object),函式(function),陣列(array))

關於記憶體的面試題

引用: 一個廣義物件(object,array,function) 例如 var a= {xxx:'xxx'} a存的是{xxx:'xxx'}這個廣義物件的地址,假如是10000。那麼a(或a存的地址10000)就是這個物件({xxx:'xxx'})的引用

var a = 1 var b = a b = 2 請問 a 顯示是幾? 2 1 (深拷貝)

var a = {name: 'a'}
var b = a
b = {name: 'b'}
複製程式碼

請問現在 a.name 是多少? 'a'

在這裡插入圖片描述

var a = {name: 'a'}
var b = a
b.name = 'b'
複製程式碼

請問現在 a.name 是多少? 'b'

在這裡插入圖片描述

var a = {name: 'a'}
var b = a
b = null
複製程式碼

請問現在 a 是什麼? {name: 'a'}

****

GC垃圾回收

如果一個物件沒有被引用,他就是垃圾,就會被回收(沒有引用找不到他,所以要回收)

在這裡插入圖片描述
{name:'a'}就是垃圾,就要被回收釋放記憶體
在這裡插入圖片描述
在這裡插入圖片描述
不會回收,因為document.body.onclick這個引用在棧記憶體中存了fu這個函式的地址

記憶體洩漏:在ie6的時候,如果關閉頁面,一些垃圾是沒有被清除,記憶體被永久的佔用了

深拷貝

  • 首先:值的儲存方法:兩種。基礎型別六個存入stack****棧記憶體,複合型別物件存入heap****堆記憶體

所以:

  • 對於六個基本資料型別來說,直接賦值就是深拷貝,因為他們直接存在棧記憶體裡。
  • 但是如果吧舊的物件賦值給新宣告的物件,那麼實際上只是把棧記憶體裡的地址賦值給了新物件,實際上堆記憶體裡仍然是原來的物件,如果修改新物件裡面的屬性,那麼原來的物件也會改變,因為用的是同一塊堆記憶體。直接給新物件賦值,無法實現拷貝。

如何實現物件的淺與深拷貝?

  1. 物件淺拷貝
  • 淺拷貝 首先可以通過Object.assign 來解決這個問題。
    
    let a = {
        age: 1
    }
    let b = Object.assign({}, a)
    a.age = 2
    console.log(b.age) // 1
    複製程式碼
    Object.assign()只拷貝第一層,如果屬性存的是一個引用,那麼他也只拷貝到引用。所以是淺拷貝
  • 深拷貝 深拷貝 這個問題通常可以通過JSON.parse(JSON.stringify(object))來解決。
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製程式碼

但是該方法也是有侷限性的:

  • 會忽略 undefined
  • 不能序列化函式
  • 不能解決迴圈引用的物件 但是可以解決大部分問題。

作用域

JavaScript有函式級作用域,僅僅函式能建立新作用域。

變數的作用域表示這個變數存在的上下文

setTimeout中的函式所處在於全域性作用域中,所以函式中使用this關鍵字時,這個this關鍵字指向的是全域性物件window

作用域鏈

題目:

var a = 1
function f1(){
  var a = 2
  console.log(a)
  f4()
  
}
function f4(){
  console.log(a)
}

f1()
複製程式碼

答案,2,1 這個f4裡面的a只能是他自己本身的作用域他的父作用域,跟f1裡面的a沒有關係 先看面試題 題目1

var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //2
複製程式碼

題目2

var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn() //1
複製程式碼

題目3

var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a

    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //undefined
複製程式碼

解密

  1. 函式在執行的過程中,先從自己內部找變數
  2. 如果找不到,再從建立當前函式所在的作用域去找, 以此往上
  3. 注意找的是變數的當前的狀態

變數提升

兩個題目

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();
複製程式碼

答案是10

第二個例子

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);
複製程式碼

alert輸出了1

變數提升後的順序 提升時程式碼表現上的排序為:

1 var a;
2 function f (){}
3 形參
4 thisarguments
複製程式碼

總結: var 宣告變數,只會提升var a=undefined(自動賦值為undefined)。 function宣告函式,function f (){},所有的都會提升。

let與const暫時死區

{ console.log(x) // Uncaught ReferenceError: x is not defined let x = 1 } let x = 1之前到花括號{,就是x的暫時死區,不用使用變數x,const一樣

instanceof

instanceof運算子用來驗證,一個物件是否為指定的建構函式例項obj instanceof Object返回true,就表示obj物件是Object的例項。

new

  1. 使用new命令時,它後面的函式依次執行下面的步驟。

    1. 建立一個空物件,作為將要返回的物件例項。
    2. 將這個空物件的原型,指向建構函式的prototype屬性。
    3. 將這個空物件賦值給函式內部的this關鍵字。
    4. 開始執行建構函式內部的程式碼。

既:

  1. 新生成了一個物件(新生成一個臨時物件)
  2. 連結到原型(新生成的物件 新物件.__proto__=建構函式.prototype
  3. 繫結 this ( this = 新物件)
  4. 返回新物件 new應用舉例:

第一步寫私有屬性,第二步寫共有屬性.

P4gxL4.png

可以看到這個物件的

在這裡插入圖片描述
1自有屬性 2__proto__指向的原型物件含有共有屬性. 3 共共有屬性(原型的屬性)constructor指向的建構函式

ES5的Object.create()

現有的物件作為模板,生成例項物件,這時就可以使用Object.create()方法。

var person1 = {
  name: '張三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

var person2 = Object.create(person1);

person2.name // 張三
person2.greeting() // Hi! I'm 張三.
複製程式碼

this,this的實質:

  • 引擎會將函式單獨儲存在記憶體中,然後再將函式的地址賦值給變數。
  • 由於函式是一個單獨的值,所以它可以在不同的**環境(上下文)**執行。
  • JavaScript 允許在函式體內部,引用當前環境的其他變數。
  • 現在問題就來了,由於函式可以在不同的執行環境執行,所以需要有一種機制,能夠在函式體內部獲得當前的執行環境(context)。所以,this就出現了,它的設計目的就是在函式體內部,指代函式當前的執行環境

總結一句話:this就是指函式所在的當前環境(既所在的物件)

this:

  • 在建構函式裡,this 代表新生成的例項物件
  • 其他情況下,this就是屬性方法當前”所在的物件。它總是返回一個物件

面試回答:;

  1. fn() 裡面的 this 就是 window(fn在最頂層)
  2. fn()strict modethis 就是 undefined(fn在最頂層,嚴格模式下是undefined)
  3. a.b.c.fn() 裡面的 this 就是 a.b.c(屬性或方法當前所在的物件。)
  4. new F() 裡面的 this 就是新生成的例項
  5. () => console.log(this) 裡面 this 跟外面的 this 的值一模一樣(ES6新增語法)(箭頭函式裡,this就上一層的this的值) 面試答上面的幾個↑

面試回答call() apply() bind():

1 call()方法,可以指定函式內部this指向(即函式執行時所在的作用域),然後在所指定的作用域中呼叫該函式。 (這個所指定的作用域意思就是所指定的物件)

var obj = {};

var f = function () {
  return this;
};

f() === window // true
f.call(obj) === obj // true
複製程式碼

call後面的引數則是函式呼叫時所需的引數

2 apply()call()唯一的區別就是,它接收一個陣列作為函式執行時的引數。

    x.call(obj,1,2,3)
    x.apply(obj,[1,2,3])
複製程式碼

3 bind方法用於將函式體內的this繫結到某個物件,然後返回一個新函式callapply都是繫結新this直接執行函式,而bind沒有執行,而是返回新函式,這個在一函式作為引數的一些方法裡就用bind

this面試題 帶call的面試題遵循以下兩個規則

  1. 如果call傳了引數,this就是傳的引數。

    在這裡插入圖片描述
    在這裡插入圖片描述

  2. 如果call沒傳引數或者為undefined,那麼this是window(飛嚴格模式),或者undefined(嚴格模式)

    在這裡插入圖片描述

    在這裡插入圖片描述

帶call的面試題仍然遵循規則 3.

在這裡插入圖片描述
4.
在這裡插入圖片描述

記住了,當call()的第一個引數是undefined的時候, thiswindow.

this面試題 this題目

在這裡插入圖片描述

答案: 呼叫B處的console.log().結果是options window(console.log()中console是全域性window物件裡的一個方法)

第二題:

PhftrF.md.png

答案:D Object

第三題:

PhfLZQ.md.png

答案:Object

JS 原型是什麼?

舉例

  1. var a = [1,2,3]
  2. 只有0、1、2、length 4 個key
  3. 但是a可以a.push(4) pop 這些方法
  4. a.__proto__ === Array.prototype(a是例項陣列物件,Array是建構函式)
  5. push函式 就是沿著 a.__proto__ 找到的,也就是 Array.prototype.push
  6. Array.prototype 還有很多方法,如joinpopslicespliceconcat
  7. Array.prototype 就是 a 的原型(proto)

聚完例子後用new物件舉例,說給面試官聽: 比若說

  1. 我們新建立一個建構函式

    function Person() {}
    複製程式碼
  2. 然後根據建構函式構造一個新物件

     var person1 = new Person();
     ```
    
    複製程式碼
  3. 每個函式都有一個 prototype 屬性,這個建構函式prototype 屬性指向了一個物件,這個物件呼叫該建構函式而建立的例項原型

  4. 當我們給Personprototypename屬性賦值為'Kevin'

    Person.prototype.name = 'Kevin';
    var person1 = new Person();
    var person2 = new Person();
    console.log(person1.name) // Kevin
    console.log(person2.name) // Kevin
    複製程式碼

    每一個新的例項物件物件都會從原型"繼承"屬性,例項物件擁有該原型的所有屬性。

  5. 說白了,原型就是 建構函式 用來 構造 新例項 的 模板物件。

  6. 這就是原型。

什麼是原型鏈?

先回答什麼是原型。在上面,然後繼續從__proto__開始往下說。

說:

  1. JavaScript物件除了 null 都具有的一個屬性,叫__proto__,這個屬性會指向該物件的原型物件。

  2. 當讀取例項的屬性時,如果找不到,就會通過__proto__查詢原型中的屬性,如果還查不到,就去找原型原型

  3. 例如Person.prototype這個原型的原型就是Object這個建構函式的prototype,既Object.prototype這個原型物件。然後,Person.prototype.__proto__就指向Object.prototype這個原型。然後Object.prototype原型是null

  4. 這些原型物件通過__proto__像鏈子一樣連起來,就叫做原型鏈。 然後給面試官畫:

鏈子上都畫上__proto__ person1----->Person.prototype----->Object.prototype----->null

Array.prototype----->Object.prototype----->null

在這裡插入圖片描述

例項的.__proto__ 指向建構函式. prototype(原型物件) 面試題:

Number.prototype.__proto__ === Object.prototype
//true
String.prototype.__proto__ === Object.prototype
//true
Boolean.prototype.__proto__ === Object.prototype
//true
Array.prototype.__proto__ === Object.prototype
複製程式碼
//同理
Function.prototype.__proto__=== Object.prototype//true
複製程式碼

關於 Function只需記住下面: 建構函式的原型:

String.__proto__===Function.prototype//true
Number.__proto__===Function.prototype//true
Boolean.__proto__===Function.prototype//true

Object.__proto__ === Function.prototype//true
複製程式碼

特殊的:

Function.__proto__===Function.Prototype//true
複製程式碼

constructor

每一個建構函式的prototype屬性都指向原型物件。 每一個原型物件的constructor屬性都指向建構函式。 原型物件

在這裡插入圖片描述

    console.log(Person === Person.prototype.constructor); // true
複製程式碼

Array注意點、偽陣列

  • 大BUG:var a = Array(3) 一個引數,且引數為數字,那麼久宣告陣列長度為3的空陣列

    在這裡插入圖片描述

    var a = Array(3,3) 兩個引數以上的時候,裡面的引數都是陣列內部的值。 所以宣告陣列不要用new Array()的方法。

  • 陣列本質上是一個物件。

  • 陣列可以用for i和for in迴圈

  • 偽陣列:

    • 偽陣列實際是一個物件,他的__proto__指向Object.prototype,沒有pop push等方法
    • 它裡面的屬性都是數字(內容是函式所傳進來的引數)和一個length
    • JS裡面只有一個偽陣列arguments,代表函式裡面所傳入的所有的引數。
    • 在這裡插入圖片描述
  • forEach

    [1,2,3].forEach(function(value,index){
    			 		   console.log(value)
    			  		   console.log(index)
    	 			})
    複製程式碼
  • sort&join&concat&map&filter&reduce segmentfault.com/a/119000001…

函式題目

函式宣告的五種方式

在這裡插入圖片描述

注意其中一種方式:

在這裡插入圖片描述

  • 函式的name

1具名函式
 function f(x,y){
     return x+y
 }
 f.name // 'f'
 
2匿名函式
 var f
 f = function(x,y){
     return x+y
 }
 f.name // 'f'
 
3具名函式賦值
 var f
 f = function f2(x,y){ return x+y }
 f.name // 'f2'
 console.log(f2) // undefined
 
4window.Function
 var f = new Function('x','y','return x+y')
 f.name // "anonymous"
 
5箭頭函式
 var f = (x,y) => {
     return x+y
 }

f.name//"f"
複製程式碼

只要記住3、4兩種特殊的情況就好。

  • this
    • 特例:當call()的第一個引數是undefined的時候, this 是 window.
    • 當啟用嚴格模式的時候,call 裡的第一個引數是什麼,this 就是什麼
  • 函式的呼叫棧:執行的時候函式進入棧return的時候函式彈出棧
  • Stack Overflow堆疊溢位,超出call stack 函式呼叫棧。

立即執行函式

作用:建立一個獨立的作用域,避免變數汙染

(function(){alert('我是匿名函式')} ()) // 用括號把整個表示式包起來
(function(){alert('我是匿名函式')}) () //用括號把函式包起來
//下面的都是執行這個表示式,而不管返回值是什麼
!function(){alert('我是匿名函式')}() 
複製程式碼

另一種建立獨立作用域的方法是使用let

非同步函式的經典題

在這裡插入圖片描述
下面程式碼列印出的是6 因為setTimeout是非同步函式,主執行緒的同步任務執行完畢,非同步任務才從任務佇列裡拿出來執行。 即使時間是0,他也是非同步的。也要等到所有程式碼解析完。等程式碼解析完畢,i已經是6了。

在這裡插入圖片描述
上面這個同理,當我點選按鈕的時候,點選事件才觸發(非同步),相當於是js早就解析完了程式碼,i已經變成6了,才執行事件繫結,事件繫結與觸發都是非同步的

閉包經典面試題

經典面試題,迴圈中使用閉包解決 var 定義函式的問題

for ( var i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log( i );
	}, i*1000 );
}
首先因為 setTimeout 是個非同步函式,所有會先把迴圈全部執行完畢,這時候 i 就是 6 了,所以會輸出一堆 6。

解決辦法兩種,第一種使用閉包(建立的這個立即執行函式就是一個新塊級作用域,並使用了外部的變數i,解析完之後,也不隨著i的最終改變而改變)+立即執行函式

for (var i = 1; i <= 5; i++) {
  (function(j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}


第三種就是使用 let 定義 i 了

for ( let i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log( i );
	}, i*1000 );
}
因為對於 let 來說,他會建立一個塊級作用域,相當於剛才的閉包建立的塊級作用域。
複製程式碼

===

=== 而不用==。只需要注意兩點

  1. NaN ===NaN//false
  2. 所有即使是內容一樣的物件=== 也都是false 。因為地址不一樣。

輪播

  • 高階輪播 // 第一步,迴圈給按鈕新增點選事件 //第二步:新增定時器,定時出發,迴圈輪播,並且進入時暫停輪播 jsbin.com/funumujoqe/…

  • 無縫輪播 說思路就行了,然後說我在工作的時候都不手寫錄播,主要是為了理解他的原理,然後用別人寫好的輪播庫swiper,這樣工作中的BUG會少一點。

回撥函式

callback 是一種特殊的函式,這個函式被作為引數傳給另一個函式去呼叫。這樣的函式就是回撥函式。 舉一個Callback(回撥函式)的例子 ,例如:

$button.on('click', function(){})
div.addEventListener('click', function(){})
複製程式碼

click 後面的 function 是一個回撥,因為「我」沒有呼叫過這個函式,是瀏覽器在使用者點選 button 時呼叫的。

再舉一個使用回撥函式的例子

function setClock(callBack){
  console.log('1定一個鬧鐘,三秒鐘之後響');
  setTimeout(()=>{
    console.log('2三秒到了,鬧鐘響了!');
    callBack();
  },3000)
}

function getUp(){
  console.log('3鬧鐘已經響了,該起床了')
}

setClock(getUp);
複製程式碼

回撥函式一般只和**非同步操作(setTimeOut)**在一起才使用!!!

什麼是非同步

1單執行緒模式

JavaScript 只在一個執行緒上執行,JavaScript 同時只能執行一個任務其他任務都必須在後面排隊等待

2同步任務(synchronous)和非同步任務(asynchronous)

同步任務主執行緒上排隊執行的任務。只有前一個任務執行完畢,才能執行後一個任務。

非同步任務不進入主執行緒、而進入任務佇列的任務。只有引擎認為某個非同步任務可以執行了(比如 Ajax 操作從伺服器得到了結果,或者setTimeOut到時間了(事件迴圈)),該任務(採用回撥函式的形式)才會進入主執行緒執行

3任務佇列和事件迴圈

JavaScript 執行時,除了一個正在執行的主執行緒,引擎還提供一個任務佇列(task queue),裡面是各種需要當前程式處理的非同步任務。

首先,主執行緒會去執行所有的同步任務。等到同步任務全部執行完,就會去看任務佇列裡面的非同步任務。如果滿足條件,那麼非同步任務就重新進入主執行緒開始執行,這時它就變成同步任務了。等到執行完,下一個非同步任務再進入主執行緒開始執行。一旦任務佇列清空,程式就結束執行。

非同步任務的寫法通常是回撥函式。一旦非同步任務重新進入主執行緒,就會執行對應的回撥函式。如果一個非同步任務沒有回撥函式,就不會進入任務佇列,也就是說,不會重新進入主執行緒,因為沒有用回撥函式指定下一步的操作。

JavaScript 引擎怎麼知道非同步任務有沒有結果,能不能進入主執行緒呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的非同步任務,是不是可以進入主執行緒了。這種迴圈檢查的機制,就叫做事件迴圈(Event Loop)。

問關於非同步、主執行緒、事件迴圈(Event Loop)的時候:按照下面回答: 面試官問問題:js是單執行緒模式的,那麼他是怎麼實現非同步操作的? 答:

  1. js裡面的任務分為同步任務非同步任務
  2. 同步任務進入主執行緒一個一個得執行。非同步任務進入任務佇列,等同步任務執行完了之後,只有觸發了某個條件,才把任務佇列裡面的任務放到主執行緒執行(比如ajax得到返回的資料,就開始執行回撥函式,setTimeOut的時間到了,就執行回撥函式)
  3. 非同步任務的寫法通常是回撥函式。一旦非同步任務重新進入主執行緒,就會執行對應的回撥函式。
  4. 同步任務執行完之後引擎就一遍一遍得檢查。JavaScript 引擎怎麼知道非同步任務有沒有結果,能不能進入主執行緒呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的非同步任務,是不是可以進入主執行緒了。這種迴圈檢查的機制,就叫做事件迴圈(Event Loop)。事件迴圈機制一遍一遍得檢查,符合條件,就把非同步任務放到主執行緒裡面去執行(比如ajax返回的資料到了,setTimeOut裡面的時間到了)

非同步操作的方法: 1.回撥函式 2.事件監聽(觸發條件,執行回撥函式) 3.ES6:Promise

定時器

它們向任務佇列新增定時任務。時間到就新增,然後事件迴圈就會掃到,掃到了就執行裡面的回撥函式

Promise

什麼是Promise? 舉例: $.ajax().then(成功函式,失敗函式)

作用:避免回撥巢狀,使回撥變的可控

ES6劃入標準標準。Promise現在是js的內建物件。Promise 物件是 JavaScript 的非同步操作的解決方案。Promise 可以讓非同步操作寫起來,就像在寫同步操作的流程,(鏈式使用,then之後可以繼續then)而不必一層層地巢狀回撥函式

寫一個Promise非同步操作解決方案

function dosomething(){
    // Promise 接受函式作為引數兩個引數,
    // resolve: 非同步事件成功時呼叫
    // reject: 非同步事件失敗時呼叫
    return new Promise((resolve, reject) => {
        let result = 非同步操作()
	// 下面給出承諾,面對不同的結果,我會 執行 不同的解決方案
	if (result === 'success') 
	    resolve('成功資料')
	else 
	    reject('失敗資料')
	})
}

// 非同步操作,模擬概率事件
function 非同步操作() {
    return Math.random() > 0.5 ? 'success' : 'fail'
}

// 你在dosomething

dosomething()
    // 非同步操作成功了,那麼我們列印成功的資料
    .then(res => console.log(res))
    //  非同步操作失敗了,那麼我們列印失敗的資料
    .catch(res => console.log(res))
複製程式碼

Sync:同步的意思 Async:非同步的意思

MVC與模組化

傳統模組化方法:

  1. 使用window
    //module1.js
    !function(){
        var person = window.person = {
            name:"frank",
        }
        //區域性變數person和全域性變數person用的是同一個地址
    }.call()
    複製程式碼
    雖然不同變數,但是是同樣的地址
    //module2.js
    !function(){
        var person = person;//即var person = window.person;
        console.log(person);
    }.call();
    複製程式碼
  2. 使用閉包
 //module1.js
!function(){
    var person = {
        name:"mataotao",
        age:18,
    };
    window.mataotaoGrowUp = function(){
        person.age+=1;
        return person.age;
    };
}.call();
複製程式碼
//module2.js
!function(){
    var newAge = window.mataotaoGrowUp();
    console.log(newAge);//19
}.call();
複製程式碼

用閉包的好處:

  • 用來 隱藏資料細節 (不告訴你多少歲但是你可以讓他漲一歲,隱藏了age 的細節和name)
  • 可以用來 做訪問控制 (只能訪問到age,無法訪問到name)

前端面試題(移動適配,閉包,this,HTTP狀態嗎,排序思路,頁面載入,陣列去重)

2 移動端是怎麼做適配的?

2016年騰訊前端面試題: 移動端是怎麼做適配的? 回答要點:

  1. meta viewport
  2. 媒體查詢
  3. 動態 rem 方案

(可以參考我寫的部落格 CSS5:移動端頁面(響應式) CSS9:動態 REM-手機專用的自適應方案) 答:

2.1做手機端頁面首先要加上一個meta標籤

    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
複製程式碼

content="width=device-width表示寬度等於裝置寬度,意思就是不要將頁面寬度變成980px,用裝置寬度. user-scalable=no表示使用者不以縮放 initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0 初始縮放倍數,最大縮放倍數,最小縮放倍數,都是1.0,即不能縮放

2.2媒體查詢

通過媒體查詢,根據不同條件,使用不同的css樣式。 例如:

    <style>
        @media (max-width: 800px){/*如果媒體滿足0到800 之間,那麼會應用這裡面的樣式*/
            body{
                background-color: red;
            }
        }
    </style>
複製程式碼

2.3動態rem

因為手機需要相容很多不同寬度的手機裝置,所以將長度單位依賴於手機裝置寬度,使用動態rem方案,那麼就可以在不同手機上實現相同比例的頁面縮放而不影響佈局。 rem:root em,即<html>font-size. 實現動態rem,主要需要下面兩步: 1在<head>標籤里加上如下程式碼,讓10rem等於頁面寬度

    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
      <title>動態REM</title>
      <script>
        var pageWidth = window.innerWidth
         document.write('<style>html{font-size:'+pageWidth+'px;}</style>')
      </script>
    </head>
複製程式碼

2使用sass自動將設計稿的px轉換為rem 在scss檔案裡寫這樣一個函式:

    @function px( $px ){
    @return $px/$designWidth*10 + rem;
    }

    $designWidth : 640; // 640 是設計稿的寬度,你要根據設計稿的寬度填寫。設計師的設計稿寬度需要統一
複製程式碼

就可以使用px()函式將畫素轉化為rem。

3 實現圓角矩形和陰影怎麼做?

2017年騰訊前端實習面試題(二面): 用過CSS3嗎? 實現圓角矩形和陰影怎麼做? (搜尋MDN border-radius) 答: 用過。例如陰影,圓角,動畫,漸變和過渡 1.圓角: 簡寫屬性border-radius。例如 border-radius: 30px; border-radius: 50%; 半徑引數可以是長度單位,也可以是百分比單位。

也可以分別設定四個角

border-top-left-radius:     4px 2px;
border-top-right-radius:    3px 4px;
border-bottom-right-radius: 6px 2px;
border-bottom-left-radius:  3px 4px;
複製程式碼

半徑引數可以是一個或兩個,一個引數代表圓形圓角,兩個引數是橢圓圓角

2.陰影:

語法: box-shadow:inset x-offset y-offset blur-radius spread-radius color 五個引數分別是:投影方式 X軸偏移量 Y軸偏移量 陰影模糊半徑 陰影擴充套件半徑 陰影顏色

4 什麼是閉包,閉包的用途是什麼?

出處同上(一面二面都問了): 什麼是閉包,閉包的用途是什麼? JavaScript高程P178 閉包的用途

答:

4.1什麼是閉包

閉包是指有權訪問另一個函式作用域中的變數的函式。 例如

function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar
}

var func = foo()
func()
複製程式碼

bar函式可以訪問變數local,bar就是一個閉包。

4.2閉包的用途

  1. 模仿塊級作用域
    function A(num) {
        //核心程式碼
       (funnction(){
        for(var i = 0; i<num; i++) {
          num++;
        }
        })()
        //核心程式碼結束
        console.log(i)//underfined
      }
    複製程式碼

匿名自執行函式在內部形成了一個閉包,使i變數只有塊級作用域。閉包的本質是函式,其實在這裡閉包就是那個匿名函式,這個閉包可以得到函式A內部的活動變數,又能保證自己內部的變數在自執行後直接銷燬。

  1. 儲存變數 閉包的另一個特點是可以儲存外部函式的變數,原理是基於javascript中函式作用域鏈的特點,內部函式保留了對外部函式的活動變數的引用,所以變數不會被釋放
function B(){
   var x = 100;
   return {
       function(){
           return x
       }
   }
 }
var m = B()//執行B函式,生成活動變數 x被m引用
複製程式碼

執行B函式,生成活動變數 x被m引用, 變數x不會被銷燬。 執行B函式,返回值就是B內部的匿名函式,此時m引用了變數x,所以B執行後x不會被釋放,利用這一點,我們可以把比較重要或者計算耗費很大的值存在x中,只需要第一次計算賦值後,就可以通過m函式引用x的值,不必重複計算,同時也不容易被修改。 3. 封裝私有變數

function Person(){
   var name = 'default';
   this.getName:function(){
       return name;
   }
   this.setName:function(value){
       name = value;
   }
}
console.log(Person.getName())//default
console.log(Person.setName('mike'))
console.log(Person.getName())//mike
複製程式碼

設定了兩個閉包函式來操作Person函式內部的name變數,除了這兩個函式,在外部無法再訪問到name變數,name也就相當於是私有成員。

5 call、apply、bind 的用法分別是什麼?

阮一峰的javascript教程--this 深入淺出 妙用Javascript中apply、call、bind

答:

如果在函式中包含多層的this,this的指向是不確定的。需要把this固定下來,避免出現意想不到的情況。JavaScript提供了call、apply、bind這三個方法,來切換/固定this的指向。

5.1Function.prototype.call()

函式例項的call方法,可以指定函式內部this的指向(即函式執行時所在的作用域),然後在所指定的作用域中,呼叫該函式。

var obj = {};

var f = function () {
  return this;
};

f() === window // true
f.call(obj) === obj // true
複製程式碼

call的第一個引數就是this所要指向的那個物件,後面的引數則是函式呼叫時所需的引數。

5.2Function.prototype.apply()

apply方法的作用與call方法類似,也是改變this指向,然後再呼叫該函式。唯一的區別就是,它接收一個陣列作為函式執行時的引數。

apply方法的第一個引數也是this所要指向的那個物件,如果設為null或undefined,則等同於指定全域性物件。第二個引數則是一個陣列,該陣列的所有成員依次作為引數,傳入原函式。原函式的引數,在call方法中必須一個個新增,但是在apply方法中,必須以陣列形式新增

function f(x, y){
  console.log(x + y);
}

f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
複製程式碼

5.3Function.prototype.bind()

bind方法用於將函式體內的this繫結到某個物件,然後返回一個新函式

bind方法的引數就是所要繫結this的物件。

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1
複製程式碼

上面程式碼中,counter.inc方法被賦值給變數func。這時必須用bind方法將inc內部的this,繫結到counter,否則就會出錯。

6 HTTP 狀態碼

出處同上: 請說出至少 8 個 HTTP 狀態碼,並描述各狀態碼的意義。

例如:

狀態碼 200 表示響應成功。

答:

狀態碼 202 表示:伺服器已接受請求,但尚未處理。 狀態碼 204 表示:請求處理成功,但沒有資源可返回。 狀態碼 206 表示:伺服器已經成功處理了部分 GET 請求。

狀態碼 301 表示:請求的資源已被永久的分配了新的 URI。 狀態碼 302 表示:請求的資源臨時的分配了新的 URI。

狀態碼 400 表示:請求報文中存在語法錯誤。 狀態碼 401 表示:傳送的請求需要有通過 HTTP 認證的認證資訊。 狀態碼 403 表示:對請求資源的訪問被伺服器拒絕了。 狀態碼 404 表示:伺服器上無法找到請求的資源。

狀態碼 500 表示:伺服器端在執行請求時發生了錯誤。 狀態碼 503 表示:伺服器暫時處於超負債或正在進行停機維護,現在無法處理請求。

7 寫出一個 HTTP 請求和響應的內容

出處同上: 請寫出一個 HTTP post 請求的內容,包括四部分。 其中 第四部分的內容是 username=ff&password=123 第二部分必須含有 Content-Type 欄位 請求的路徑為 /path

看我的部落格HTTP入門(一):在Bash中curl檢視請求與響應

答: 請求:

1 POST /path HTTP/1.1
2 Host: www.baidu.com
2 User-Agent: curl/7.20.0 (x86_64-unknown-linux-gnu) libcurl/7.20.0 zlib/1.2.8
2 Accept: */*
2 Content-Length: 24
2 Content-Type: application/x-www-form-urlencoded
3 
4 username=ff&password=123
複製程式碼

響應:

1 HTTP/1.1 200 OK
2Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
2Content-Length: 2443
2Content-Type: text/html(百度返回的時候百度的資料長度和內容的格式)
2Etag: "5886041d-98b"
2Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
3
4<!DOCTYPE html> ...
複製程式碼

1234567890

8 請說出至少三種排序的思路

這三種排序的時間複雜度分別為

O(n*n) O(n log2 n) O(n + max)

答:

O(n*n) 氣泡排序:遍歷整個陣列,依次比較相鄰兩個元素,將小的排在前面,大的排後面,這樣一遍迴圈下來就可以將最大的元素排到最後,除去已經排過的最大的數,然後再次迴圈以上操作,直到最後一個為止。

O(n log2 n) 快速排序:以第一個元素為基準,比這個元素小的元素排在左邊,比這個元素大的排右邊,再以該元素左邊和右邊的第一個元素為基準,在子區間重複以上的操作,直到只有一個數字排序為止。

O(n + max) 基數排序:首先根據個位數的數值,將需要排序的一串數值分配到0-9的桶中。接著將這些桶中的數值重新串起來,形成新的數列。接著根據十位數、百位數直至最高位重複以上操作。

9 頁面從輸入URL到頁面載入顯示完成的過程

著名前端面試題:

一個頁面從輸入 URL 到頁面載入顯示完成,這個過程中都發生了什麼? 這一題是在挖掘你的知識邊界,所以你知道多少就要答多少。

可以先查閱一些資料再查,但是不要把自己不懂的東西放在答案裡,面試官會追問的。

知乎上:從輸入 URL 到頁面載入完成的過程中都發生了什麼 答:

  1. DNS解析 DNS解析的過程就是瀏覽器查詢域名對應的 IP 地址;
  2. TCP連線 瀏覽器根據 IP 地址向伺服器發起 TCP 連線,與瀏覽器建立 TCP 三次握手: (1)主機向伺服器傳送一個建立連線的請求(您好,我想認識您); (2)伺服器接到請求後傳送同意連線的訊號(好的,很高興認識您); (3)主機接到同意連線的訊號後,再次向伺服器傳送了確認訊號(我也很高興認識您),自此,主機與伺服器兩者建立了連線。
  3. 傳送HTTP請求 瀏覽器根據 URL 內容生成 HTTP 請求報文。HTTP請求報文是由三部分組成: 請求行, 請求報頭和請求正文,其中包含請求檔案的位置、請求檔案的方式等等。
  4. 伺服器處理請求並返回HTTP報文

伺服器接到請求後,回想客戶端傳送HTTP響應報文。HTTP響應報文也是由三部分組成: 狀態碼, 響應報頭和響應報文。伺服器會根據 HTTP 請求中的內容來決定如何獲取相應的 HTML 檔案,並將得到的 HTML 檔案傳送給瀏覽器。

  1. 瀏覽器解析渲染頁面 瀏覽器是一個邊解析邊渲染的過程。在瀏覽器還沒有完全接收 HTML 檔案時便開始渲染、顯示網頁。在執行 HTML 中程式碼時,根據需要,瀏覽器會繼續請求圖片、CSS、JavsScript等檔案,過程同請求 HTML 。

  2. 關閉TCP連線或繼續保持連線

(1)主機向伺服器傳送一個斷開連線的請求(不早了,我該走了);

(2)伺服器接到請求後傳送確認收到請求的訊號(知道了);

(3)伺服器向主機傳送斷開通知(我也該走了);

(4)主機接到斷開通知後斷開連線並反饋一個確認訊號(嗯,好的),伺服器收到確認訊號後斷開連線;

10 如何實現陣列去重

著名面試題: 如何實現陣列去重? 假設有陣列 array = [1,5,2,3,4,2,3,1,3,4] 你要寫一個函式 unique,使得 unique(array) 的值為 [1,5,2,3,4] 也就是把重複的值都去掉,只保留不重複的值。

要求:

不要做多重迴圈,只能遍歷一次 請給出兩種方案,一種能在 ES 5 環境中執行,一種能在 ES 6 環境中執行(提示 ES 6 環境多了一個 Set 物件) 從 JavaScript 陣列去重談效能優化 也談JavaScript陣列去重 答:

ES5: 思路:核心是構建了一個 hash 物件來替代 indexOf. 注意在 JavaScript 裡,物件的鍵值只能是字串,因此需要 var key = typeof(item) + item 來區分數值 1 和字串 '1' 等情況。 只迴圈一遍

function unique(arr) {
  var ret = []
  var hash = {}
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i]
    var key = typeof(item) + item
    if (hash[key] !== 1) {
      ret.push(item)
      hash[key] = 1
    }
  }
  return ret
}
複製程式碼

ES6:ES2015引入了一種叫作Set的資料型別。顧名思義,Set就是集合的意思,它不允許重複元素出現。 如果重複新增同一個元素的話,Set中只會存在一個。包括NaN也是這樣

function unique(array) {
   return Array.from(new Set(array));
}
複製程式碼

JS題目總結:原型鏈/new/json/MVC/Promise

1原型鏈相關

P4oVZ8.png
解讀: 上圖中,Object,Function,Array,Boolean都是構造函式

第一個框: object是例項物件,他的模板物件(原型物件)在Object()建構函式裡面. 建構函式.prototype指向的是原型物件,即模板物件. 由建構函式構造出來的例項物件.__proto__也指向的是原型物件,即模板物件. 所以true.

第二個框: fn是一個例項函式,是由用來構造出函式建構函式造出來的. 所以fn.__proto__ === Function.prototype

任何建構函式.prototype都是一個物件. 因為fn.__proto__ === Function.prototype 所以fn.__proto__.__proto__ === Object.prototype等價於 Function.prototype.__proto__ === Object.prototype 等價於 一個物件.__proto__ === Object.prototype 所以是true

第三個框同理.

第四個框比較難理解: 一個例項函式是由用來構造出函式建構函式造出來的.

Object,Function,Array都是一個例項函式,函式也是一種型別,就像String是一種型別,Number是一種型別一樣,函式這個型別裡的例項函式由函式的建構函式造出來!很難理解 所以例項函式.__proto__===建構函式.prototype 例項函式的建構函式就是Function

有點雞生蛋蛋生雞的感覺.

第五個框同理

2物件導向,new,原型鏈相關

function fn(){
    console.log(this)
}
new fn()
複製程式碼

new fn() 會執行 fn,並列印出 this,請問這個 this 有哪些屬性?這個 this 的原型有哪些屬性? 答: 這個this就是new建立的新物件. this(這個新物件)有__protot__屬性,它指向fn建構函式的原型即fn.prototype 這個原型(即fn.prototype)有兩個屬性:

  1. construct :它的值是建構函式fn
  2. __proto__: 它指向Object.prototype

解讀:

P5Ctu8.png

  1. fn()是建構函式
  2. new fn()就是一個建構函式new出來的新物件. 他的自有屬性為空,共有屬性為空,因為都沒有設定 因為他的自有屬性為空,所以他只有一個__proto__指向建構函式.prototype(即原型)了. 共有屬性為空,所以他的原型就是隻有constructor指向建構函式和__proto__指向Object.prototype(因為原型本身就是物件型別,所以指向物件的建構函式) 例子:

P5U2pd.png

P5aqUO.png

4 MVC

前端 MVC 是什麼?(10分) 請用程式碼大概說明 MVC 三個物件分別有哪些重要屬性和方法。(10分)

答一:

MVC 是什麼 MVC 是一種設計模式(或者軟體架構),把系統分為三層:Model資料、View檢視和Controller控制器。 Model 資料管理,包括資料邏輯、資料請求、資料儲存等功能。前端 Model 主要負責 AJAX 請求或者 LocalStorage 儲存 View 負責使用者介面,前端 View 主要負責 HTML 渲染。 Controller 負責處理 View 的事件,並更新 Model;也負責監聽 Model 的變化,並更新 View,Controller 控制其他的所有流程。

答二: MVC就是把程式碼分為三塊

V(view)只負責看得見的東西. M(model)只負責跟資料相關的操作,不會出現DOM,不會出現任何的html/css操作.例如model裡只會有初始化資料庫,獲取資料方法fetch(),儲存資料的方法save() C(controller)只負責把這些view和model組合起來,找到view,找到model,使用model完成資料修改業務,並修改view的顯示 V:檢視 M:資料 C:控制器

MVC是一種程式碼組織形式,不是任何一種框架,也不是任何一種技術,只是組織程式碼的思想,要做的就是V和M傳給C,C去統籌 在js裡,MVC分別由三個物件去擔任三個職責

程式碼一:

window.View = function(xxx){
    return document.querySelector(xxx);
}
複製程式碼
window.Model = function(object){
    let resourceName = object.resourceName;
    return {
        init: function () { 
        },
        fetch: function () { 
        },
        save: function (object) {
        }
    }
}
複製程式碼
window.Controller = function(options){
    var init = options.init;
    let object = {
        view:null,
        model:null,
        init:function(view,model){
            this.view = view;
            this.model = model;
            this.model.init();
            init.call(this,view,model);
            this.bindEvents();
        },
        bindevnets:function(){},
    };

    for (let key in options) {
        if(key !=='init'){
            object[key] = options[key]
        }
    };
    return object;
}
複製程式碼

程式碼二:

var model = {
    data: null,
    init(){}
    fetch(){}
    save(){}
    update(){}
    delete(){}
}
view = {
    init() {}
    template: '<h1>hi</h1'>
}
controller = {
    view: null,
    model: null,
    init(view, model){
        this.view = view
        this.model = model
        this.bindEvents()
    }
    render(){
        this.view.querySelector('name').innerText = this.model.data.name
    },
    bindEvents(){}
}
複製程式碼

5 ES5類,原型鏈,建構函式,new

如何在 ES5 中如何用函式模擬一個類?(10分)

答一:

使用原型物件,建構函式,new來模擬類.

  1. 將公共屬性放到原型物件裡,並且將建構函式的prototype屬性指向原型物件.
  2. 私有屬性(自有屬性)放到建構函式裡去定義.
  3. 將例項化的物件的__proto__指向原型物件. 這樣當建構函式建立一個例項化的物件的時候,就即擁有自己的私有變數和方法,也有公有的變數和方法了,例項化出來的物件的私有方法和變數修改都不會互相有影響,只有在修改公有的變數和方法的時候是對所有例項生效的

答二: ES 5 沒有 class 關鍵字,所以只能使用函式來模擬類。

function Human(name){
    this.name = name
}
Human.prototype.run = function(){}

var person = new Human('frank')
複製程式碼

上面程式碼就是一個最簡單的類,Human 建構函式建立出來的物件自身有 name 屬性,其原型上面有一個 run 屬性。

陣列方法,快排,冒泡,陣列去重

陣列方法

Array.isArray() Array.isArray(arr) // true

2.例項方法 valueOf(),toString() arr.valueOf() // [1, 2, 3]返回本身 arr.toString() // "1,2,3,4,5,6"

push(),pop() shift()//刪除第一個元素

join()//變成一個字串

concat()//陣列合並

reverse()//顛倒陣列

slice(起始位置,終止位置)//[起始位置,終止位置)減下來一部分(一小片)陣列,一小片,返回一個新陣列,原陣列不變

splice方法用於刪除原陣列的一部分成員,並可以在刪除的位置新增新的陣列成員,返回值是被刪除的元素。注意,該方法會改變原陣列。(裁剪下來)

var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]
複製程式碼

從下標4開始,往後刪2個

sort()//按字典序排序,可以傳入函式作為引數規定排序的方式。直接改變了原陣列

遍歷: map()//對映,並返回一個新陣列,裡面函式可以調整陣列的值

forEach()//forEach方法不返回新陣列,只用來遍歷資料

filter()//過濾一個陣列 [1, 2, 3, 4, 5].filter(function (elem) { return (elem > 3); }) // [4, 5]

indexOf()//indexOf方法返回給定元素在陣列中第一次出現的位置. 如果沒有出現則返回-1

String的方法

屬性:length

方法: var s = new String('abc'); s.charAt(1) // "b" 這個方法完全可以用陣列下標替代。 'abc'.charAt(1) // "b" 'abc'[1] // "b"

concat()

slice()減一小塊下來,返回新字串,[起始位置,結束位置) substring()一樣,優先使用slice() substr()一樣,優先使用slice()

indexOf() 用於確定一個字串在另一個字串中第一次出現的位置,返回結果是匹配開始的位置。如果返回-1,就表示不匹配

trim()去除字串兩端的空格,返回新字串

toLowerCase() toUpperCase() 變換大小寫

split()將字串分割為陣列 'a|b|c'.split('|') // ["a", "b", "c"],與jion相對應

Math

Math.floor(),Math.ceil()地板值,天花板值,相對於數軸的。

Math.round()四捨五入

Math.random() [0,1)的隨機數

快排

let a = [1,1,1,2,2.2,12,14,23,443,5,5,435,45656768,4,6,7,5,8,33,67,86,2,86,67,845,4,24,5342,1,2]
function quickSort (arr){
  if(arr.length<=1) {return arr}
  //   let midIndex = Math.floor(arr.length/2)
  //   let midItem = arr.splice(0,1)[0]
  let midItem = arr[0]
  let left = []
  let right = [] 
  for(i = 0;i<arr.length;i++){
    if(i!==0){//跳過中間值
      if(arr[i]<midItem){
        left.push(arr[i])
      }else{
        right.push(arr[i])
      }
    }
  }
  return quickSort(left).concat([midItem],quickSort(right))
}
console.log(quickSort(a))
複製程式碼

jsbin.com/ravujoyopi/…

陣列去重

let a=[1,1,1,2,2,2,3,3,3,3,undefined,'xxx','xxx','3','3','undefined']

function f(array){
  let hash = {}
  let returnArr = []
  for(let i=0;i<array.length;i++){
    let arrItemStr = (typeof array[i]) + array[i] + ""//防止undefined和'undefined'轉換成字串然後衝突

    if(hash[arrItemStr]!==1){//說明未進入hash
      hash[arrItemStr]=1
      returnArr.push(array[i])
    }
  }
//   console.log(hash)
  return returnArr
}
console.log(f(a))


//2:
console.log(Array.from(new Set(a)))
複製程式碼

ES6

瞭解let const 解構賦值 箭頭函式 class promise

相關文章