daily-question-01(前端每日一題01)

在這裏記錄着天天本身遇到的一道印象深入的前端問題,以及一道生活中隨處可見的小問題。html

強迫本身造成積累的習慣,鞭撻本身不斷前行,共同窗習。前端

Github 地址vue

2019/04/01 - 2019/04/07

1.爲什麼 [] == ![] 結果爲 true,而 {} == !{} 卻爲 false

首先了解一下類型轉化的規則:node

一、若是有一個操做數是布爾值,則在比較相等性以前先將其轉換爲數值——false 轉換爲 0,而 true 轉換爲 1;git

二、若是一個操做數是字符串,另外一個操做數是數值,在比較相等性以前先將字符串轉換爲數值github

三、若是一個操做數是對象,另外一個操做數不是,則調用對象的 valueOf()(boolean 對象方法)方法或者 toString()方法,用獲得的基本類型值按照前面的規則進行比較vue-cli

null 和 undefined 是相等的express

四、要比較相等性以前,不能將 null  和  undefined  轉換成其餘任何值編程

五、若是有一個操做數是 NaN,則相等操做符返回 false ,而不相等操做符返回  true。重要提示:即便兩個操做數都是 NaN,相等操做符也返回 false 了;由於按照規則, NaN 不等於 NaN (NaN 不等於任何值,包括他自己)json

六、若是兩個操做數都是對象,則比較它們是否是同一個對象,若是兩個操做數都指向同一個對象,則相等操做符返回 true;不然,返回 false

七、 !可將變量轉換成 boolean 類型,null、undefined、NaN 以及空字符串('')取反都爲 true,其他都爲 false。

如今開始分析題目

[] == ![];
// 先轉化右邊 ![],
// `!`可將變量轉換成 boolean 類型,null、undefined、NaN 以及空字符串('')取反都爲 true,其他都爲 false。
// 因此 ![] => false => 0

// 左邊 [], 由於[].toString() 爲空字符串,因此 [] => ''

// 綜上, '' == 0, 爲 true
{} == !{}
// 先轉化右邊 !{},
// `!`可將變量轉換成 boolean 類型,null、undefined、NaN 以及空字符串('')取反都爲 true,其他都爲 false。
// 因此 !{} => false => 0

// 左邊 ({}).toString() => "[object Object]"

// 綜上, "[object Object]" == 0, 爲 false

2.RESTful 接口的優缺點

什麼是 restful 接口 ?

REST -- REpresentational State Transfer,英語的直譯就是「表現層狀態轉移」,它包含如下三個方面:

URL 設計: RESTful 的核心思想就是,客戶端發出的數據操做指令都是"動詞 + 賓語"的結構。好比,GET /articles 這個命令,GET 是動詞,/articles 是賓語。

動詞一般就是五種 HTTP 方法,對應 CRUD 操做。

  • GET:讀取(Read)
  • POST:新建(Create)
  • PUT:更新(Update)
  • PATCH:更新(Update),一般是部分更新
  • DELETE:刪除(Delete)

狀態碼: 客戶端的每一次請求,服務器都必須給出迴應。迴應包括 HTTP 狀態碼和數據兩部分。

服務器迴應: API 返回的數據格式,不該該是純文本,而應該是一個 JSON 對象,由於這樣才能返回標準的結構化數據。因此,服務器迴應的 HTTP 頭的 Content-Type 屬性要設爲 application/json。

優勢

簡潔明瞭,一目瞭然;輕量,直接經過 http,不須要額外的協議,post/get/put/delete 操做

缺點

當一次更新的內容多的時候須要調用更多的接口。刪除也是,若是我想批量刪除呢?

  1. 對後端開發人員要求高,業務邏輯有時難以被抽象爲資源的增刪改查。
  2. 對前端開發人員不友好,API 粒度較粗,難以查詢符合特殊要求的數據,一樣的業務要比普通的 API 須要更屢次 HTTP 請求。

3.對視口 viewport 的理解 ?

視口分爲:layout viewport -- 佈局視口,visual viewport -- 視覺視口,ideal viewport -- 理想視口

若是把移動設備上瀏覽器的可視區域設爲 viewport 的話,某些網站就會由於 viewport 太窄而顯示錯亂,因此這些瀏覽器就決定默認狀況下把 viewport 設爲一個較寬的值,好比 980px,這樣的話即便是那些爲桌面設計的網站也能在移動瀏覽器上正常顯示了。這個瀏覽器默認的 viewport 叫作 layout viewport。這個 layout viewport 的寬度能夠經過 document.documentElement.clientWidth 來獲取。

layout viewport 的寬度是大於瀏覽器可視區域的寬度的,因此咱們還須要一個 visual viewport 來表明瀏覽器可視區域的大小。visual viewport 的寬度能夠經過 window.innerWidth 來獲取

ideal viewport 即每一個設備完美適配的視口。所謂的完美適配指的是,第一不須要用戶縮放和橫向滾動條就能正常的查看網站的全部內容;第二是不管文字,圖片等在不一樣的設備都能顯示出差很少的效果。ideal viewport 並無一個固定的尺寸,不一樣的設備擁有有不一樣的 ideal viewport。

mata 標籤與 viewport 的關係

<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>

移動設備默認的是 layout viewport , 可是咱們須要的是 ideal viewport, 那麼經過 meta 標籤的做用就是:讓當前 viewport 的寬度等於設備的寬度,同時不容許用戶手動縮放。

meta 標籤中 content 的屬性和值以下:

  • width 設置 layout viewport 的寬度,爲一個正整數,或字符串"width-device"
  • initial-scale 設置頁面的初始縮放值,爲一個數字,能夠帶小數
  • minimum-scale 容許用戶的最小縮放值,爲一個數字,能夠帶小數
  • maximum-scale 容許用戶的最大縮放值,爲一個數字,能夠帶小數
  • height 設置 layout viewport 的高度,這個屬性對咱們並不重要,不多使用
  • user-scalable 是否容許用戶進行縮放,值爲"no"或"yes", no 表明不容許,yes 表明容許

4.移動端的像素?

一、物理像素(設備像素)

紅藍綠能夠調配出任何顏色,一般說的手機像素就是由許多紅藍綠組成的一個小塊,1 個小塊表示 1 個像素。一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,經過控制每一個像素點的顏色,使屏幕顯示出不一樣的圖像。屏幕從工廠出來那天起,它上面的物理像素點就固定不變了,單位 pt - 固定單位。

好比:iPhone六、七、8 的分辨率爲 1334*750 像素表示,橫向 750 個像素,縱向 1334 個像素

二、CSS 像素

CSS 和 JS 使用的抽象單位,瀏覽器內的一切長度都是以 CSS 像素爲單位的,CSS 像素的單位是 px。

一倍屏:當設備像素比爲 1:1 時,使用 1(1×1)個設備像素顯示 1 個 CSS 像素;

二倍屏:當設備像素比爲 2:1 時,使用 4(2×2)個設備像素顯示 1 個 CSS 像素;

三倍屏:當設備像素比爲 3:1 時,使用 9(3×3)個設備像素顯示 1 個 CSS 像素。

三、像素密度(PPI)

每英寸像素取值,也就是衡量單位物理面積內擁有像素值的狀況。

ppi 越高,每英寸像素點越多,圖像越清晰;咱們能夠類比物體的密度,密度越大,單位體積的質量就越大,ppi 越高,單位面積的像素越多。

ppi 在 120-160 之間的手機被歸爲低密度手機,160-240 被歸爲中密度,240-320 被歸爲高密度,320 以上被歸爲超高密度(例如:蘋果的 Retina 屏幕)

5.__proto__prototype 的區別 ?

  1. 在 JS 裏,萬物皆對象。方法(Function)是對象,方法的原型(Function.prototype)是對象。所以,它們都會具備對象共有的特色。即:對象具備屬性__proto__,可稱爲隱式原型,一個對象的隱式原型指向構造該對象的構造函數的原型,這也保證了實例可以訪問在構造函數原型中定義的屬性和方法。
  2. 方法(Function)方法這個特殊的對象,除了和其餘對象同樣有上述 proto 屬性以外,還有本身特有的屬性——原型屬性(prototype),這個屬性是一個指針,指向一個對象,這個對象的用途就是包含全部實例共享的屬性和方法(咱們把這個對象叫作原型對象)。原型對象也有一個屬性,叫作 constructor,這個屬性包含了一個指針,指回原構造函數。

6.雙精度浮點數是如何保存的 ?

在計算機中,浮點表示法,分爲三大部分:

第一部分用來存儲符號位(sign),用來區分正負數,0 表示正數
第二部分用來存儲指數(exponent)
第三部分用來存儲小數(fraction), 多出的末尾若是是 1 須要進位;

雙精度浮點數一共佔據 64 位:

符號位(sign)佔用 1 位
指數位(exponent)佔用 11 位
小數位(fraction)佔用 52 位

舉個例子:0.1 的二進制爲

0.00011001100110011001100110011001100110011001100110011001 10011...

轉化爲 2 進制科學計數法

1.1001100110011001100110011001100110011001100110011001 * (2 ^ -4);

也就是說 0.1 的:

  • 符號位爲:0
  • 小數位爲:1001100110011001100110011001100110011001100110011001
  • 指數位爲:-4

指數位爲負數的怎麼保存?爲了減小沒必要要的麻煩,IEEE 規定了一個偏移量,對於指數部分,每次都加這個偏移量進行保存,這樣即便指數是負數,那麼加上這個偏移量也變爲正數啦。爲了使全部的負指數加上這個偏移量都可以變爲正數,IEEE 規定 1023 爲雙精度的偏移量。

所以指數部分爲 -4 + 1023 = 1019, 轉化成 11 位二進制爲:01111111011

所以 0.1 在內存中的保存爲:

0 01111111011 1001100110011001100110011001100110011001100110011001

7.如何找出字符串中出現最多的字母 (ababccdeajxac)?

最早想到的解法是用 map 紀錄每一個字符的次數,而後找出最多的便可:

function getMaxNumberOfChar(str) {
  return (str + "").split("").reduce(
    function(pre, cur, index, arr) {
      cur in pre ? pre[cur]++ : (pre[cur] = 1);
      pre[cur] > pre.value && ((pre.char = cur), (pre.value = pre[cur]));
      return pre;
    },
    { value: 0 }
  );
}
getMaxNumberOfChar("ababccdeajxac"); // Object {value: 4, a: 4, char: "a", b: 2, c: 3…}

此外,能夠考慮用正則來輔助處理:

function getMaxNumberOfChar(str) {
  return (str + "")
    .split("")
    .sort()
    .join("")
    .match(/(\w)\1*/g)
    .reduce(
      function(pre, cur) {
        return cur.length > pre.value
          ? { value: cur.length, char: cur[0] }
          : pre;
      },
      { value: 0 }
    );
}
getMaxNumberOfChar("ababccdeajxac"); // Object {value: 4, char: "a"}

這裏拓展一下 reduce 函數的用法

// reduce 函數
// array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue)
// reducer回調函數自己接受幾個參數,第一個參數是 accumulator 累加器,第二個是數組中的 item,第三個參數是該項的索引,最後一個參數是原始數組的引用。
// initialValue 爲reduce初始值,不然視數組第一個值爲初始值,選填
const array1 = [1, 2, 3, 4];

// 1 + 2 + 3 + 4
console.log(
  array1.reduce((accumulator, currentValue) => {
    console.log(accumulator, currentValue);
    return accumulator + currentValue;
  })
);

2019/03/27 - 2019/03/31

1.請問這句語句 var args=[].slice.call(arguments,1) 是什麼意思?

先看原函數:

function a() {
  var args = [].slice.call(arguments, 1);
  console.log(args);
}

a("haha", 1, 2, 3, 4, 5); // log出[1, 2, 3, 4, 5]
a("run", "-g", "-b"); // log出['-g', '-b']
  1. 首先,函數 call() 方法,第一個參數改變函數的 this 指向,後面剩餘參數傳入原函數 slice 中
  2. arguments 是什麼?

    arguments 是函數中的一個類數組的參數集合對象
    如: {'0': 'haha', '1': 1, '2': 2}
  3. slice 爲數組可從已有的數組中返回選定的元素。

    此題爲從 index = 1 日後
  4. 綜上,這句語句的做用是——將函數中的實參值轉化成數組
    </details>

2.連等賦值問題

var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };
// a ? b ? a.x ? 結果是什麼?
<details>
  <summary>點擊</summary>

  咱們能夠先嚐試交換下連等賦值順序(a = a.x = {n: 2};),能夠發現輸出不變,即順序不影響結果。

  那麼如今來解釋對象連等賦值的問題:按照 es5 規範,題中連等賦值等價於
  a.x = (a = { n: 2 });,按優先獲取左引用(lref),而後獲取右引用(rref)的順序,a.x 和 a 中的 a 都指向了{ n: 1 }。至此,相當重要或者說最迷惑的一步明確。(a = {n: 2})執行完成後,變量 a 指向{n: 2},**並返回{n: 2}**;接着執行 a.x = { n: 2 },這裏的 a 就是 b(指向{ n: 1 }),因此 b.x 就指向了{ n: 2 }。

  賦值時有返回該值, 如 `a = 4 // return 4` , 賦值變量 `let n = 5 //return undefinded`

  ```js
  a = { n: 2 };
  b = { n: 1, x: { n: 2 } };
  a.x = undefinded;
  ```

3.如何手動實現一個 Promise ?

promise 的三種狀態 pending, resolve, reject
function MyPromise(callback) {
  let that = this;
  //定義初始狀態
  //Promise狀態
  that.status = "pending";
  //value
  that.value = "undefined";
  //reason 是一個用於描述Promise被拒絕緣由的值。
  that.reason = "undefined";
  //用來解決異步問題的數組
  that.onFullfilledArray = [];
  that.onRejectedArray = [];

  //定義resolve
  function resolve(value) {
    //當status爲pending時,定義Javascript值,定義其狀態爲fulfilled
    if (that.status === "pending") {
      that.value = value;
      that.status = "resolved";
      that.onFullfilledArray.forEach(func => {
        func(that.value);
      });
    }
  }

  //定義reject
  function reject(reason) {
    //當status爲pending時,定義reason值,定義其狀態爲rejected
    if (that.status === "pending") {
      that.reason = reason;
      that.status = "rejected";
      that.onRejectedArray.forEach(func => {
        func(that.reason);
      });
    }
  }

  //捕獲callback是否報錯
  try {
    callback(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let that = this;
  //須要修改下,解決異步問題,即當Promise調用resolve以後再調用then執行onFulfilled(that.value)。
  //用兩個數組保存下onFulfilledArray
  if (that.status === "pending") {
    that.onFullfilledArray.push(value => {
      onFulfilled(value);
    });

    that.onRejectedArray.push(reason => {
      onRejected(reason);
    });
  }

  if (that.status === "resolved") {
    onFulfilled(that.value);
  }

  if (that.status === "rejected") {
    onRejected(that.reason);
  }
};

4.AST(抽象語法樹)?

什麼是 AST(抽象語法樹)?

It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.

它是一種分層程序表示,它根據編程語言的語法呈現源代碼結構,每一個 AST 節點對應一個源代碼項。

Babel,Webpack,vue-cli 和 esLint 等不少的工具和庫的核心都是經過 Abstract Syntax Tree (抽象語法樹)這個概念來實現對代碼的檢查、分析等操做的。

解析(parsing),轉譯(transforming),生成(generation)。

將源碼解析成 AST 抽象語法樹,再對此語法樹進行相應的轉譯,最後生成咱們所須要的代碼。

第三方的生成 AST 庫有不少,這裏推薦幾個——esprima, babylon(babel 使用)

其轉化的內容大體是這樣的:

{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "FunctionDeclaration",
      "start": 0,
      "end": 16,
      "id": {
        "type": "Identifier",
        "start": 9,
        "end": 12,
        "name": "ast"
      },
      "expression": false,
      "generator": false,
      "params": [],
      "body": {
        "type": "BlockStatement",
        "start": 14,
        "end": 16,
        "body": []
      }
    }
  ],
  "sourceType": "module"
}

AST 的使用場景

  • 代碼語法的檢查、代碼風格的檢查、代碼的格式化、代碼的高亮、代碼錯誤提示、代碼自動補全等等.
  • 代碼混淆壓縮
  • 優化變動代碼,改變代碼結構使達到想要的結構

5.爲何 0.1 + 0.2 !== 0.3 ?

### IEEE-754 精度問題

全部使用 IEEE-754 數字實現的編程語言都有這個問題。

0.1 和 0.2 的二進制浮點數表示並非精確的,因此相加後不等於 0.3。這個相加的結果接近 0.30000000000000004。

首先將 0.1 轉化爲 2 進制

// 0.1  十進制 -> 二進制
0.1 * 2 = 0.2  取0
0.2 * 2 = 0.4  取0
0.4 * 2 = 0.8  取0
0.8 * 2 = 1.6  取1
0.6 * 2 = 1.2  取1
0.2 * 2 = 0.4  取0
0.4 * 2 = 0.8  取0
0.8 * 2 = 1.6  取1
0.6 * 2 = 1.2  取1
//0.000110011(0011)`   0.1二進制(0011)裏面的數字表示循環

你會發現 0.1 轉二級制會一直無線循環下去,根本算不出一個正確的二進制數。

因此咱們得出 0.1 = 0.000110011(0011),那麼 0.2 的演算也基本如上所示,因此得出 0.2 = 0.00110011(0011)

六十四位中符號位佔一位,整數位佔十一位,其他五十二位都爲小數位。由於 0.1 和 0.2 都是無限循環的二進制了,因此在小數位末尾處須要判斷是否進位(就和十進制的四捨五入同樣)

那麼把這兩個二進制加起來會得出 0.010011....0100 , 這個值算成十進制就是 0.30000000000000004

相關文章
相關標籤/搜索