js基礎(chǔ)知識(shí)

[TOC]

null和undefined區(qū)別

  • 例子
// 在代碼中
Number(null); // 0
5 + Number(null); // 5
Number(undefined); // NaN

對(duì)于null和undefined,大致可以像下面這樣理解。

null表示空值,即該處的值現(xiàn)在為空。調(diào)用函數(shù)時(shí),某個(gè)參數(shù)未設(shè)置任何值,這時(shí)就可以傳入null,表示該參數(shù)為空。比如,某個(gè)函數(shù)接受引擎拋出的錯(cuò)誤作為參數(shù),如果運(yùn)行過程中未出錯(cuò),那么這個(gè)參數(shù)就會(huì)傳入null,表示未發(fā)生錯(cuò)誤。

整數(shù)和浮點(diǎn)數(shù)

  • JavaScript 內(nèi)部,所有數(shù)字都是以64位浮點(diǎn)數(shù)形式儲(chǔ)存,即使整數(shù)也是如此。所以,1與1.0是相同的,是同一個(gè)數(shù)。
1 === 1.0 // true
  • JavaScript 會(huì)自動(dòng)把64位浮點(diǎn)數(shù),轉(zhuǎn)成32位整數(shù),然后再進(jìn)行運(yùn)算
  • js運(yùn)算有效范圍在 即-2的53次方到2的53次方

數(shù)值的精度

(-1)^符號(hào)位  *  1.xx...xx  *  2^指數(shù)部分

上述

根據(jù)國際標(biāo)準(zhǔn) IEEE 754,JavaScript 浮點(diǎn)數(shù)的64個(gè)二進(jìn)制位,從最左邊開始,是這樣組成的。

  • 第1位:符號(hào)位,0表示正數(shù),1表示負(fù)數(shù)

  • 第2位到第12位(共11位):指數(shù)部分

  • 第13位到第64位(共52位):小數(shù)部分(即有效數(shù)字)
    所以 js運(yùn)算有效范圍在 即-2的53次方到2的53次方

  • 任何數(shù)都有正負(fù)數(shù) , 包括0

  • 絕大多數(shù)的情況下 , 除了除法 , 其他運(yùn)算和0運(yùn)算都是一樣的 , 例子如下

1 / +0 // Infinity 正無窮

1 / -0 // -Infinity // 負(fù)無窮
1 / -0 === 1 / +0 // false

Infinity 特殊運(yùn)算


5 * Infinity ;// Infinity
0 * Infinity; // NaN

Infinity - Infinity; // NaN
Infinity / Infinity; // NaN

  • Infinity與null計(jì)算時(shí),null會(huì)轉(zhuǎn)成0,等同于與0的計(jì)算。Nnumber(null)輸出0
  • Infinity與undefined計(jì)算,返回的都是NaN

parseInt 和 parseFloat

  • parseInt 如果第二個(gè)參數(shù)不是數(shù)值,會(huì)被自動(dòng)轉(zhuǎn)為一個(gè)整數(shù)。這個(gè)整數(shù)只有在2到36之間,才能得到有意義的結(jié)果,超出這個(gè)范圍,則返回NaN。如果第二個(gè)參數(shù)是0、undefined和null,則直接忽略。
  • parseFloat除了不接受第二個(gè)參數(shù)和特定的進(jìn)制外 , 其他使用和parseInt一樣

isNaN()

isNaN('Hello')
// 相當(dāng)于
isNaN(Number('Hello'))

isFinite()

判斷是否為一個(gè)正常數(shù)值

Base64 轉(zhuǎn)碼

  • btoa():任意值轉(zhuǎn)為 Base64 編碼
  • atob():Base64 編碼轉(zhuǎn)為原來的值

表達(dá)式還是語句?

當(dāng)首行是大括號(hào)開頭時(shí) , js引擎會(huì)解析成代碼塊 , 想要解析成對(duì)象 , 就可以使用以下方式`


({ foo: 123 }) // 正確
({ console.log(123) }) // 報(bào)錯(cuò)

對(duì)象屬性操作

var obj = {};
delete obj.p // true

上面代碼中,對(duì)象obj并沒有p屬性,但是delete命令照樣返回true。因此,不能根據(jù)delete命令的結(jié)果,認(rèn)定某個(gè)屬性是存在的。

屬性是否存在:in 運(yùn)算符

var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true , 會(huì)自動(dòng)往原型上面查找 , 所以得使用 hasOwnProperty 判斷是否自身

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

for...in

for...in循環(huán)有兩個(gè)使用注意點(diǎn)。

  • 它遍歷的是對(duì)象所有可遍歷(enumerable)的屬性,會(huì)跳過不可遍歷的屬性。
  • 它不僅遍歷對(duì)象自身的屬性,還遍歷繼承的屬性。
    舉例來說,對(duì)象都繼承了toString屬性,但是for...in循環(huán)不會(huì)遍歷到這個(gè)屬性。

var obj = {};

// toString 屬性是存在的 , 但是默認(rèn)是不可遍歷屬性
obj.toString // toString() { [native code] }

for (var p in obj) {
  console.log(p);
} // 沒有任何輸出
  • 應(yīng)該結(jié)合使用hasOwnProperty方法,在循環(huán)內(nèi)部判斷一下,某個(gè)屬性是否為對(duì)象自身的屬性。

函數(shù)名的提升


f();

function f() {} // 不報(bào)錯(cuò)


f();
var f = function (){};
// TypeError: undefined is not a function


// 例子1和例子2解釋了如果同時(shí)采用function命令和賦值語句聲明同一個(gè)函數(shù),最后總是采用賦值語句的定義。

// 例子2和例子3解釋了 , 使用賦值語句聲明的函數(shù) , 如果執(zhí)行函數(shù)在前面那么會(huì)執(zhí)行同名的 function命令 聲明的函數(shù)
{
// 例子1
    var f = function () {
      console.log('1');
    }

    function f() {
      console.log('2');
    }

    f() // 1
}
{
// 例子2
    function f() {
      console.log('2');
    }
    var f = function () {
      console.log('1');
    }
    f() // 1
}

{
// 例子3
    f() // 輸出2
    var f = function () {
      console.log('1');
    }

    function f() {
      console.log('2');
    }
}

函數(shù)name屬性


var f3 = function myName() {};
f3.name // 'myName'


var f2 = function () {};
f2.name // "f2"

運(yùn)算符

這里特地說一下 + 運(yùn)算符
對(duì)于引用類型相加
首先兩個(gè)引用類型相加 , 都會(huì)經(jīng)過一系列的轉(zhuǎn)換才會(huì)去相加 , 最后都會(huì)轉(zhuǎn)換成基本類型

var obj = {}
obj + 2 // "[object Object]2"

他是怎么相加的呢
引用類型首先會(huì)調(diào)用對(duì)象的valueOf方法。再自動(dòng)調(diào)用對(duì)象的toString方法 , 這就是他的原理
但是也有特殊情況

{} + [] // 0 , 這里 , 前面的 {} 會(huì)被js引擎當(dāng)成代碼塊 , 這里實(shí)際只是 +[] 
[] + {} // "[object Object]"

還有一種特殊情況就是 , 當(dāng)引用類型是function時(shí) , 又會(huì)去會(huì)調(diào)用對(duì)象的valueOf方法。再自動(dòng)調(diào)用對(duì)象的toString方法
例子

{}+ function a(){} // "[object Object]function a(){}"

還有一種特殊情況 , 引用類型為Date時(shí) , Date直接調(diào)用toString方法

var obj = new Date();
obj.valueOf = function () { return 1 };
obj.toString = function () { return 'hello' }; // 我們可以對(duì)toString直接改寫

obj + 2 // "hello2"

自增和自減運(yùn)算符

執(zhí)行該運(yùn)算子之前 , 都會(huì)先對(duì)前面需要運(yùn)算的函數(shù)或者值進(jìn)行Number處理 , 而且會(huì)改變?cè)贾?br> 例如當(dāng) 當(dāng)前是引用類型錯(cuò)誤的時(shí)候

var A = {}
A++ //NaN
console.log(A) // NaN
var a = function(){}
a++  //NaN
console.log(a) // NaN

指數(shù)運(yùn)算符

這個(gè)指數(shù)運(yùn)算符相當(dāng)于使用Math.pow(x,y)
例子

2 ** 3
// 跟下面相等
Math.pow(2,3)

特殊情況 , 指數(shù)運(yùn)算符是右結(jié)合,而不是左結(jié)合。即多個(gè)指數(shù)運(yùn)算符連用時(shí),先進(jìn)行最右邊的計(jì)算

2 ** 3 ** 2// 512
// 相當(dāng)于 
2 ** (3 ** 2)

比較運(yùn)算符

比較大小的算法是

  1. 當(dāng)兩個(gè)比較的運(yùn)算子為字符串的時(shí)候 按照字典順序比較(實(shí)際上是比較 Unicode 碼點(diǎn))
  2. 其他情況 , 將兩個(gè)運(yùn)算子都轉(zhuǎn)成數(shù)值,再比較數(shù)值的大小。

其他運(yùn)算 ,
NaN , 所有類型的數(shù)值和NaN 比大小都是false
對(duì)象和引用類型的類型的值 , 會(huì)轉(zhuǎn)換成原始類型的值再做比較
先調(diào)用 valueOf() 再調(diào)用 toString() ,
還有其他比較 , 例如


false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

不相等運(yùn)算符相等運(yùn)算符有一個(gè)對(duì)應(yīng)的“不相等運(yùn)算符”(!=),它的算法就是先求相等運(yùn)算符的結(jié)果,然后返回相反值。

1 != '1' // false

// 等同于
!(1 == '1')

布爾運(yùn)算符

!!x
// 等同于
Boolean(x)

短路運(yùn)算
如下


if (i) {
  doSomething();
}

// 等價(jià)于

i && doSomething();

二進(jìn)制位運(yùn)算符

二進(jìn)制位運(yùn)算符用于直接對(duì)二進(jìn)制位進(jìn)行計(jì)算,一共有7個(gè)。

  1. 二進(jìn)制或運(yùn)算符(or):符號(hào)為|,表示若兩個(gè)二進(jìn)制位都為0,則結(jié)果為0,否則為1。
  2. 二進(jìn)制與運(yùn)算符(and):符號(hào)為&,表示若兩個(gè)二進(jìn)制位都為1,則結(jié)果為1,否則為0。
  3. 二進(jìn)制否運(yùn)算符(not):符號(hào)為~,表示對(duì)一個(gè)二進(jìn)制位取反。
  4. 異或運(yùn)算符(xor):符號(hào)為^,表示若兩個(gè)二進(jìn)制位不相同,則結(jié)果為1,否則為0。
  5. 左移運(yùn)算符(left shift):符號(hào)為<<,詳見下文解釋。
  6. 右移運(yùn)算符(right shift):符號(hào)為>>,詳見下文解釋。
  7. 頭部補(bǔ)零的右移運(yùn)算符(zero filled right shift):符號(hào)為>>>,詳見下文解釋。

這些位運(yùn)算符直接處理每一個(gè)比特位(bit),所以是非常底層的運(yùn)算,好處是速度極快,缺點(diǎn)是很不直觀,許多場合不能使用它們,否則會(huì)使代碼難以理解和查錯(cuò)

!!!注意點(diǎn)1 有一點(diǎn)需要特別注意,位運(yùn)算符只對(duì)整數(shù)起作用
!!!注意點(diǎn)2 做位運(yùn)算的時(shí)候,是以32位帶符號(hào)的整數(shù)進(jìn)行運(yùn)算的,并且返回值也是一個(gè)32位帶符號(hào)的整數(shù)
!!!注意點(diǎn)3 位運(yùn)算只對(duì)整數(shù)做運(yùn)算 , 且會(huì)自動(dòng)去除小數(shù)點(diǎn) , 且內(nèi)部js引擎會(huì)調(diào)用Number()方法

// 利用這個(gè)特性,可以寫出一個(gè)函數(shù),將任意數(shù)值轉(zhuǎn)為32位整數(shù)
function toInt32(x) {
  return x | 0;
}

異或運(yùn)算符

異或運(yùn)算(^)在兩個(gè)二進(jìn)制位不同時(shí)返回1,相同時(shí)返回0
例子

// 表達(dá)式中,0(二進(jìn)制00)與3(二進(jìn)制11)進(jìn)行異或運(yùn)算,它們每一個(gè)二進(jìn)制位都不同,所以得到11(即3)
0 ^ 3 // 3

還有另外一個(gè)超實(shí)用的用法 , 就是不使用臨時(shí)變量的情況下 , 互換兩個(gè)變量的整數(shù)


var a = 10;
var b = 99;

a ^= b, b ^= a, a ^= b;

a // 99
b // 10

類型轉(zhuǎn)換

parseInt逐個(gè)解析字符,而Number函數(shù)整體轉(zhuǎn)換字符串的類型。
一般來說Number非數(shù)字字符串 , 就會(huì)返回NaN

Number原理

第一步,調(diào)用對(duì)象自身的valueOf方法。如果返回原始類型的值,則直接對(duì)該值使用Number函數(shù),不再進(jìn)行后續(xù)步驟。
第二步,如果valueOf方法返回的還是對(duì)象,則改為調(diào)用對(duì)象自身的toString方法。如果toString方法返回原始類型的值,則對(duì)該值使用Number函數(shù),不再進(jìn)行后續(xù)步驟。
第三步,如果toString方法返回的是對(duì)象,就報(bào)錯(cuò)。

String方法 , 原理

與Number方法基本相同,只是互換了valueOf方法和toString方法的執(zhí)行順序。

  1. 先調(diào)用對(duì)象自身的toString方法。如果返回原始類型的值,則對(duì)該值使用String函數(shù),不再進(jìn)行以下步驟。
  2. 如果toString方法返回的是對(duì)象,再調(diào)用原對(duì)象的valueOf方法。如果valueOf方法返回原始類型的值,則對(duì)該值使用String函數(shù),不再進(jìn)行以下步驟。
  3. 如果valueOf方法返回的是對(duì)象,就報(bào)錯(cuò)。

Boolean() 原理

Boolean()函數(shù)可以將任意類型的值轉(zhuǎn)為布爾值。

它的轉(zhuǎn)換規(guī)則相對(duì)簡單:除了以下五個(gè)值的轉(zhuǎn)換結(jié)果為false,其他的值全部為true。

  1. undefined
  2. null
  3. 0(包含-0和+0)
  4. NaN
  5. ''(空字符串)

關(guān)于try .... catch .... finally

例子


try {
    throw '出錯(cuò)了!';
  } catch(e) {
    console.log('捕捉到內(nèi)部錯(cuò)誤');
    throw e + '這里是catch'; // 這句原本會(huì)等到finally結(jié)束再執(zhí)行 , 但由于finally有return語句 , 就不會(huì)執(zhí)行
  } finally {
    return false; // 直接返回
  }
  /* 輸出如下
  捕捉到內(nèi)部錯(cuò)誤 ------ > 這里是catch執(zhí)行的邏輯
 false ----> 這里是finally return 返回的false
  */
  
  // 如果我們不在finally使用return語句 , 那么就是執(zhí)行catch里面拋出錯(cuò)誤的邏輯 , 例子如下
  try {
    throw '出錯(cuò)了!';
  } catch(e) {
    console.log('捕捉到內(nèi)部錯(cuò)誤');
    throw e + '這里是catch'; // 這句原本會(huì)等到finally結(jié)束再執(zhí)行 , 但由于finally有return語句 , 就不會(huì)執(zhí)行
  } finally {
    console.log('finally'); // 執(zhí)行這里會(huì) , 返回catch里面拋出錯(cuò)誤
  }

編寫風(fēng)格

對(duì)switch函數(shù)改寫

switch...case結(jié)構(gòu)要求,在每一個(gè)case的最后一行必須是break語句,否則會(huì)接著運(yùn)行下一個(gè)case。這樣不僅容易忘記,還會(huì)造成代碼的冗長。

而且,switch...case不使用大括號(hào),不利于代碼形式的統(tǒng)一。此外,這種結(jié)構(gòu)類似于goto語句,容易造成程序流程的混亂,使得代碼結(jié)構(gòu)混亂不堪,不符合面向?qū)ο缶幊痰脑瓌t。
可以使用以下改寫風(fēng)格

// 原switch case

function doAction(action) {
  switch (action) {
    case 'hack':
      return 'hack';
    case 'slash':
      return 'slash';
    case 'run':
      return 'run';
    default:
      throw new Error('Invalid action.');
  }
}

// 改寫如下

function doAction(action) {
  var actions = {
    'hack': function () {
      return 'hack';
    },
    'slash': function () {
      return 'slash';
    },
    'run': function () {
      return 'run';
    }
  };

  if (typeof actions[action] !== 'function') {
    throw new Error('Invalid action.');
  }

  return actions[action]();
}

Object 對(duì)象

Object.prototype.toString.call(obj)判斷類型
不同數(shù)據(jù)類型的Object.prototype.toString.call(obj)方法返回值如下。

  1. 數(shù)值:返回[object Number]。
  2. 字符串:返回[object String]。
  3. 布爾值:返回[object Boolean]。
  4. undefined:返回[object Undefined]。
  5. null:返回[object Null]。
  6. 數(shù)組:返回[object Array]。
  7. arguments 對(duì)象:返回[object Arguments]。
  8. 函數(shù):返回[object Function]。
  9. Error 對(duì)象:返回[object Error]。
  10. Date 對(duì)象:返回[object Date]。
  11. RegExp 對(duì)象:返回[object RegExp]。
  12. 其他對(duì)象:返回[object Object]。

this

  • 將this當(dāng)作foreach方法的第二個(gè)參數(shù),固定它的運(yùn)行環(huán)境。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}

o.f()

// hello a1
// hello a2

這就是說,Object.prototype.toString可以看出一個(gè)值到底是什么類型。

call

函數(shù)實(shí)例的call方法,可以指定函數(shù)內(nèi)部this的指向(即函數(shù)執(zhí)行時(shí)所在的作用域),然后在所指定的作用域中,調(diào)用該函數(shù)
簡單的例子


var obj = {};

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

f() === window // true  ----> this指向window
f.call(obj) === obj // true ---> this指向obj

call環(huán)境如果是null , undefined , 或者不傳參數(shù) , 那么就是指向window


var n = 123;
var obj = { n: 456 };

function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

如果call方法的參數(shù)是一個(gè)原始值,那么這個(gè)原始值會(huì)自動(dòng)轉(zhuǎn)成對(duì)應(yīng)的包裝對(duì)象,然后傳入call方法。

例子

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

f.call(5)
// Number {[[PrimitiveValue]]: 5}

call第一個(gè)參數(shù)是作用域 , 其余參數(shù)是對(duì)象接收的參數(shù) , ,不返回新的函數(shù)
apply第一個(gè)參數(shù)是作用域, 第二個(gè)參數(shù)是數(shù)組作為對(duì)象的參數(shù)傳進(jìn)去 , 不返回新的函數(shù)
bind , 使用方式和call一樣 , 但是會(huì)返回新的函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容