夯實基礎,徹底掌握js的核心技術(一)

由於工作項目原因,最近很少更新博客了,不過忙中抽閒,利用晚上時間,總結了一些有關JS的基礎知識,並分享一些大廠面試 題,根據知識點進行具體分析講解,希望能對方便大家來學習。

數據類型/堆棧內存

JS中的數據類型

  1. 基本數據類型

number string boolean null undefined symbol bigint

  1. 引用數據類型

object function<br />數據類型結構如下圖

數字類型中的比較奇怪的值

  1. NaN是number類型<br />Console.log(typeof NaN) // number<br />Console.log(NaN === NaN)<br />object.is(NaN, NaN) // true
  2. 檢測一個值是否爲有效數字 isNaN<br />Console.log(isNaN(10)) // false<br />console.log(isNaN(‘AA’)) // 在檢測的時候,如果當前這個值不是數字類型,先隱士轉換爲數字類型(Number),然後再檢測是否爲非有效數字 true
  3. Infinity無窮大的值

數據類型轉換方法

  1. typeof

    檢測出來的結果是字符串,字符串 中包含了我們對於的數據類型<br /> typeof null 檢測結果是 object ,不過null並不屬於對象,而是因爲二進制儲存值以000開頭了,檢測對象細分類型,所以結果是“object”<br />

  2. instanceof

  3. constructor

  4. Object.prototype.toString.call([value])

let res = parseFloat('left:200px'); // NaN
if(res === 200) {
    alert(200)
} else if (res === NaN) {
    alert(NaN)
} else if(typeof res === 'number') {
    alert('number')  // alert輸出的結果都會轉換成字符串
} else {
    alert('Invalid Number')
}
// number
let a = typeof typeof typeof[12, 23];
console.log(a) //string
// 解析 typeof[12, 23] => "object" 
// typeof "object" => "string"
// typeof "string" => "string"

數據類型轉換的4大核心標準

  1. 把其他數據類型轉換爲Number類型
  • 特定需要轉換爲Number的

    • Number([val])<br /> + parseInt/parseFloat([val])
  • 隱式轉換(瀏覽器內部默認要先轉換爲Number在進行計算的)

  • isNaN([val]) <br />+ 數學運算(特殊情況:+在出現字符串的情況下不是數學運算,是字符串拼接)<br />+ 在==比較的時候,有些值需要轉換爲數字再進行比較
  1. 把其它數據類型轉換爲字符串
  • 能使用的辦法
  • toString()<br />+ String()
  • 隱式轉換(一般都是調用其toString)

    • 加號運算的時候,如果某一邊出現字符串,則是字符串拼接<br />+ 把對象轉換爲數字,需要先toString()轉換爲字符串,再去轉換爲數字<br />+ 基於alert/confirm/prompt/document.write...這些方式輸出內容,都是把內容先轉換爲字符串,然後再輸出的<br />
  1. 在==比較的過程中,數據轉換的規則
  • 類型一樣的幾個特殊點

    {}=={}:false 對象比較的是堆內存的地址<br /> []==[]:false<br />NaN==NaN:false

  • 類型不一樣的轉換規則

null==undefined:true,但是換成===結果是false(因爲類型不一致),剩下null/undefined和其它任何數據類型值都不相等<br />字符串==對象 要把對象轉換爲字符串<br /> 剩下如果==兩邊數據類型不一致,都是需要轉換爲數字再進行比較

  1. 把其它數據類型轉換爲布爾
  • 基於以下方式可以把其它數據類型轉換爲布爾

    • ! 轉換爲布爾值後取反<br /> + !! 轉換爲布爾類<br />+ Boolean([val])
  • 在循環或者條件判斷中,條件處理的結果就是布爾類型值

  • 在循環或者條件判斷中,條件處理的結果就是布爾類型值<br />注意:規則:只有 ‘0、NaN、null、undefined、空字符串’ 五個值會變爲布爾的FALSE,其餘都是TRUE<br />把其他類型轉換爲字符串,一般都是直接“”包起來,只有{}普通對象調取toString是調取的Object.prrototype.toString,不是轉換爲字符串,而是檢測數據類型,返回結果是"[object Objecg]"<br />
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
console.log(result); // NaNTencentnull9false
/*
    * 隱式轉換
  true 轉換爲1
  null 轉換爲0
  undefined 轉換爲NaN
  [] 轉換爲 ""
*/

let result2 = 10 + false + undefined + [] + 'lanfeng' + null + true + {};

// 10 + false => 10+ 0 => 10
// 10 + undefined => 10 + NaN => NaN
// NaN + [] => NaN + "" => "NaN"

// "NaN" + "lanfeng" => "NaNlanfeng"
// {} => "[object Object]"
// "NaNlanfengnulltrue[object Object]"

/*
*字符串 字符串拼接
* 對象 對象+ 0 需要把對象轉換爲字符串
*/
console.log([]==false); 
//對象 == 布爾 都轉換成數字(隱式轉換),對象轉換成數字:先toString轉換爲字符串
//(應該是先基於valueOf獲取原始值,沒有原始值再去toString),再轉換爲數字的
// 1. [] => '' => 0
// 2. false => 0
// 0 == 0 
// true


console.log(![]==false); //![]優先false, false == false true
onsole.log([]===false); // false

console.log(![]==false); //true
// ![] 把數組轉換成爲布爾類型值後,然後取反, 爲false
//false === false 
// true

var a = {
 i:0,
  toString() {
    return ++this.i
  }
}
if(a==1 && a==2 && a==3) {
    console.log('OK')
}

let arr = [10, 18, 0, 10, 25, 23]
arr = arr.map(parseInt)
console.log(arr)

把其他類型轉換爲數字

  1. Number機制

對象變爲數字,應該先valueOf,沒有原始值,再toString變爲字符串,最後把字符串轉爲數字

console.log(Number('')) // 0
console.log(Number('10')) // 10
console.log(Number('10px')) // NaN 只要出現非有效數字字符串結果都是NaN
console.log(Number(true)) // 1
console.log(Number(false)) // 0
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
  1. parseInt機制,從字符串左側第一個字符開始,查找有效數字字符(遇到非有效數字字符停止查找,不論後面是否還有數字字符,都不再找了),把找到的有效數字字符轉爲數字,如果一個都沒找到結果返回NaN(parseFloat比它多識別一個小數點)
parseInt("") //NaN
Number("") // 0
isNaN("") // 轉爲0,0是有效數字,所以結果是false
parseInt(null)  // parseInt("null") ,結果是NaN
isNaN(null) // null => 0 0是有效數字,所以結果是false
parseInt("12px") // 12
Number("12px") // NaN
isNaN("12px") // true
parseFloat("1.6px") + parseInt("1.2px") + typeof parseInt(null)
// 1.6+1+"number" => 2.6+ "number" => "2.6number"

isNaN(Number(!!Number(parseInt("0.8")))) // false
// parseInt("0.8") => 0
// !!0 => false
// Number(false) => 0
// isNaN(0) => false

typeof !parseInt(null) + !isNaN(null) // "booleantrue"
// parseInt(null) => NaN
// !NaN => true
// typeof true => "boolean"
// isNaN(null) => false
// !false => true
// => "booleantrue"

注意:

  1. 在js 中加號左右兩邊出現字符串,則變爲字符串拼接(有特殊性),如果出現對象也會變爲字符串拼接(因爲原本應該是把對象轉爲數字,但是對象轉數字需要先轉換爲字符串,則+遇到字符串直接轉變爲字符串拼接)
  2. 加號即使一邊出現字符串或者對象,也不一定是字符串拼接:++i/i++/+i,這種情況是數學運算
let n = "10"
console.log(++n) // 11
console.log(+n) //10
{} + 0 // 0
// 左邊的{}認爲是一個代碼塊,不參與運算,運算只處理 +0=>0
{{}+0} // 
// 參與到數學運算中"[object Object]0"
0 + {}  參與到數學運算中"0[object Object]"

<br />**<br />

變量提升問題

var a = 0;
if (true) {
    a = 1;
    function a() {};
    a = 21;
    console.log(a)
}
console.log(a);

當前上下文代碼執行之前,會把var/function聲明+定義,帶var的只聲明,帶function聲明+定義,如果遇到了{}新老瀏覽器表現還好不一致(兼容ES3、ES6)

  • IE瀏覽器 <= IE10

不管{},還是一如既往的function聲明+定義,而且也不會存在塊級作用域

  • 新版本瀏覽器

{}中的function,在全局下只聲明不再定義,{} 中出現的function/let/const會創建一個塊級上下文<br />

閉包作用域

var x = 1;
function func(x, y = function anonymous1() {x = 2}) {
    x = 3;
    y();
    console.log(x); // 2
}
func(5);
console.log(x); // 1

var x = 1;
function func(x, y = function anonymous1() {x = 2}) {
   /*
     * EC(FUNC)私有上下文
     *    作用域鏈:<EC(FUNC),EC(G)>
     *    x=5  (2)
     *    y=anonymous1   [[scope]]:EC(FUNC)
     * 
     * EC(BLOCK) 塊級上下文 (上級上下文 EC(FUNC))
     *    變量提升:var x;  
     *    在代碼沒有執行之前,我們會把EC(FUNC)中的值也給他一份  x=5  (3)
     */
    var x = 3; //=>跨級上下文中的x  x=3
    y();  //=>不是塊級的y,向上級找, EC(FUNC)
  
  // anonymous1執行 
    // 私有的上下文EC(AN)  作用域鏈:<EC(AN),EC(FUNC)>
    // x=2 修改的是EC(FUNC)中的2
    console.log(x); // 3
}
func(5);
console.log(x); // 1

ES6中存在塊級作用域(只要{} [除對象之外的大括號] 出現let/const/function)<br />
<br />有一種情況也會產生

  • 函數有形參賦值了默認值
  • 函數體中有單獨聲明過某個變量

這樣在函數運行的時候,會產生兩個上下文<br /> 第一個:函數執行形成的私有上下文 EC(FUNC) =>作用域鏈/形參賦值/..<br /> 第二個:函數體大括號包起來的是一個塊級上下文 EC(BLOCK)

面向對象

function Dog(name) {
    this.name = name;
}
Dog.prototype.bark = function () {
    console.log('wangwang');
}
Dog.prototype.sayName = function () {
    console.log('my name is ' + this.name);
}
function _new(func,...args) {
    //=>完成你的代碼
  let obj = Object.create(func.prototype)
  let result = func.call(obj, ...args)
  if(result !== null && /^(object | functiion)$/.test(typeof(result))) return result
  return obj

}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true
// 手寫call方法
~function(){
    function change(context, ...args){
        //=>實現你的代碼
     // this -> func
        context = context == undefined ? window : context;
        let type = typeof context;
        if (!/^(object|function)$/.test(type)) {
            if (/^(symbol|bigint)$/.test(type)) {
                context = Object(context);
            } else {
                context = new context.constructor(context);
            }
        }
        let key = Symbol('key'),
            result;
        context[key] = this;
        result = context[key](...args);
        delete context[key];
        return result;
    };
    Function.prototype.change=change;
}();
let obj = {name:'zhufeng'};
function func(x,y){
    this.total=x+y;
    return this;
}
let res = func.change(obj,100,200);
//res => {name:'zhufeng',total:300}

總結

這篇文章主要分享了javascript數據類型、數據類型轉換、變量提升、閉包作用域、面向對象及一些一線面試題,如果想了解更多,請掃描二維碼,關注公衆號<br />

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