那些很熟卻又不是很熟的知識

本文爲知識整理,可能工做中用到這些東西很少,但是總有人想讓你會html


前言:小時候很渴望長大,羨慕大人們的財富自由;長大後又很羨慕小時候,懷念小時候的無憂無慮,守候着那份天真。哦,還有,不是長大就能財富自由。。。html5

一:js繼承


①:對象冒充實現繼承:(可實現多繼承)數組


——原理:讓父類構造函數成爲子類的方法,而後調用子類的方法,經過this關鍵字給全部屬性和方法賦值瀏覽器

function Parent(name)
{
    this.name=name;
    this.sayName=function () {
        console.log(this.name);
    }
}
function Child (cname) {
    this.parent=Parent;
    this.parent(cname);
    delete this.parent; // 刪除無用的parent函數 == f Parent(name)
}
var mychild=new Child("名字");
mychild.sayName();

②:原型鏈繼承 (不能實現多繼承)app

prototype框架


function Parent (name, age) {
      this.name = name;
      this.age = age;
      this.ParFunc = function () {
          console.log(this.height)
      }
    };
    Parent.prototype.sayName = function () {
      console.log(this)
    };
    function Child (cname, cage, height) {
      this.height = height
    }

Child.prototype = new Parent(); // Child原型指向Parent的一個實例
Child.prototype.constructor = Child; // 把Child指向本身,不與Parent共享
var child = new Child('測試名字', '測試年齡', '測試身高')
console.log(child) // 發現打印出來的屬性都繼承了,就是沒值

child.ParFunc():當訪問ParFunc屬性時,會先在child的實例屬性中尋找,找不到就去child的原型對象上去找。一層一層的尋找構成了原型鏈
clipboard.png
由於沒法向父類構造函數傳參;能夠 new Parent('名字', '年齡')這時傳參
注:若是想用原型給Child拓展方法:async

Child.prototype.childHeight = function () {
      console.log(this.height)
    }

必定要寫到Child.prototype = new Parent()的下面,要麼就被覆蓋掉了。函數

③:call、apply繼承(不能繼承原型鏈,prototype上的)測試


function Parent (name, age) {
          this.name = name;
          this.age = age;
          this.ParFunc = function () {
              console.log(this.name)
          }
        }
        
        function Child (cname, cage, height) {
          Parent.call(this,cname,cage); // 繼承的參數爲當前函數的形參
          // apply: Parent.call(this,arguments);
          this.height = height;
        }
        var child = new Child('測試名字', '測試年齡', '測試身高')
        console.log(child)  //  ParFunc: ƒ () age: "測試年齡"
name: "測試名字"

④:組合繼承:call+原型鏈繼承this


function Parent (name, age) {
  this.name = name;
  this.age = age;
  this.ParFunc = function () {
      console.log(this.height)
  }
}
Parent.prototype.sayName = function () {
  console.log(this)
}
function Child (cname, cage, height) {
  Parent.call(this, cname, cage); // 解決傳參問題
  this.height = height;
};
Child.prototype = new Parent()
Child.prototype.constructor = Child; // 把Child指向本身,不然一直指向Parent
var child = new Child('測試名字', '測試年齡', '測試身高')

比較經常使用的繼承方式,缺點是 兩次調用 Parent構造函數

⑤:寄生組合繼承:

cal+prototype


function Parent (name, age) { // 父函數
  this.name = name;
  this.age = age;
  this.ParFunc = function () {
      console.log(this.height)
  }
}
Parent.prototype.sayName = function () {
  console.log(this)
}
function Child (cname, cage, height) { // 子函數
  Parent.call(this, cname, cage)
  this.height = height;
};
var createObj = function () { // 中間函數繼承 Parent
  function Trans() {};
  Trans.prototype = Parent.prototype;
  return new Trans();
};
Child.prototype = createObj()
Child.prototype.constructor = Child; // 改回指針
var child = new Child('名字', '年齡', '身高')
console.log(child)

二:如何獲取自定義屬性,特例data-*如何獲取

官方定義:

data-*是 **html5** 新屬性
    主要用於存儲頁面的自定義數據
    不該該包含大寫字母(會默認轉爲小寫)
    註釋:用戶代理會徹底忽略前綴爲 "data-" 的自定義屬性。

腦海裏第一印象就是 getAttribute(),setAttribute()倆屬性了,一個獲取,一個設置
而平時又不多用到,可是平時用的框架什麼的多數都用 data-* 這個自定義屬性,那其實獲取 data- 這類自定義屬性的時候,還有個更方便的方法dataset

<div data-a="aa" id="div1" data-b="bb"></div>
eg:var div1 = document.getElementById('div1')
console.log(div1.dataset) // DOMStringMap {a: "測試", b: "222"}a: "測試"b: "222"

用data-*做爲自定義屬性:能夠一句話就獲取到全部屬性,獲取方式也簡便

三:事件的幾個階段:捕獲、目標(event.target,即觸發過程)、冒泡

先盜個圖

clipboard.png
——冒泡(IE事件流):從最深的節點向外傳播,div -> window,就比如往水裏丟一個石子,一層層波浪抵達河邊緣

——捕獲(網景):從最外層向目標傳遞,window -> div,就比如你去一個大企業集團找人,須要提供公司 > 大部門 > 小部門 > 小組 > 目標

——目標:即觸發過程event.target

——target、currentTarget的區別:target這個屬性指向的是目標過程當中的DOM對象,也就是 觸發事件監聽的對象, currentTarget這個指向的是當前的對象,具體內容跟this同樣,當this指向的是目標的時候(事件的目標階段),target與currentTarget相同

——如今幾乎全部的主流瀏覽器都支持冒泡;IE早起版本會跳過html直接跳到document,且不支持捕獲。

——平時多數用到冒泡多一些,事件代理(委託)也是經過事件冒泡的原理,讓DOM節點可追溯,也能夠利用冒泡的原理作埋點,避免新增DOM節點,改代碼上線

——事件句柄addEventListener('事件名', 'function', false),默認冒泡

四:判斷數據類型,返回數據的具體類型

emm... 那不就直接 return typeof n 不就完了,哦不對,再識別一下數組,由於數組的typeof也是對象,Array.isArray(n)...
/^12/ 這返回啥? wc,也是object吧,那咋區分,對,正則有test方法,再判斷一下

if (n.test) {
    return 'RegExp'
}

null好像也返回obj吧,時間 Date嘞,都返回obj也沒毛病,萬物皆對象啊。

  • 聽說instanceof也能夠:左側是不是右側的實例,也就是說每一個類型咱們都得判斷,因而
[] instanceof Array // true
[] instanceof Object // true

不光麻煩,返回的也不精確啊

  • 聽說constructor也能夠:js引擎會爲函數添加prototype,並讓其指向'該函數'的引用

/^12/.constructor // f RexExp(){[native code]}
new Date().constructor // ƒ Date() { [native code] }
null.constructor // 報錯:Cannot read..
undefined.constructor // 報錯:Cannot read..
發現確實能校驗一些typeof 不能校驗的,可是 null和undefined沒有'指針'啊,並且寫繼承的時候,指針是能夠被改的,稍不注意就涼涼了...

  • 把這些都整合到一塊兒基本也都夠用了,但是並不優雅

有請toString登場....
華麗分割線


toString() 是 Object 的原型方法,調用該方法,默認返回當前對象的 [[Class]] 。這是一個內部屬性,其格式爲 [object Xxx] ,其中 Xxx 就是對象的類型。完美~~

Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]

可是我以爲除了obj比較特殊以外,其餘類型,typeof均可以判斷,不須要再多調用一次toString方法,因此最終封裝 =>

function typeDetection (n) {
  if (typeof n === 'object') {
      return Object.prototype.toString.call(n)
  } else {
      return typeof n
  }
}

直接調用typeDetection('') // string

五:千分位的實現

Q:字符:1234567890.12 轉換爲:1,234,567,890.12

R:
我的用了while循環的方式

function strFilter (str) {
  let newStr = String(str).split('.') // 切分原始字符,兼容傳入Number類型
  let transStr = newStr[0],resStr = [] 
  while (transStr/1000 > 1) { // 判斷是否大於1000
      let whildStr = String((transStr/1000).toFixed(3)).split('.') // 這裏必定要保留三位小數,不然正數部分末位的0就會丟失,又轉爲String,由於Number沒有split方法
      transStr = whildStr[0] // 每次都取小數點之前的(正數部分)
      resStr.unshift(whildStr[1]) // 向前插入小數點後的數字()
  }
  // 除以1000剩下的數 + 每次除以1000後的數 + 原來小數
  let res2 = newStr[1]?('.' + newStr[1]):''
  let resComma = resStr.length?(',' + resStr.join(',')): ''
  return transStr + resComma + res2
}

雖然實現代碼不少,但我的以爲還算易懂

網上看到用正則的,確實簡短:

function strFilter2 (n) {
  let s = String(n)
  let re = /\d{1,3}(?=(\d{3})+$)/g
  let n1 = s.replace(/^(\d+)((\.\d+)?)$/, function (s, s1, s2) { return s1.replace(re, '$&,') + s2 })
  return n1
}

其實正則在好多場景都體現出優點,只是不能被輕易想到

Q:如下this指向

(function () {
  "use strict";
  console.log(this) // undefined
})()
(function () {
  "use strict";
  console.log(this) // window
})()

R:嚴格模式下,除構造函數、對象內置函數外,this均指向 undefined

Q:script標籤的 async、defer啥區別?
R:別說那沒用的,上圖

clipboard.png
啥也不加:script讀取和解析腳本階段都會阻斷頁面執行,
加async : script解析腳本階段會阻斷頁面執行
加defer : scriptj腳本將在頁面完成解析時執行

Q:[1,2,3].map(parseInt)的結果?
R:以前用到parseInt,只知道是向下取整,翻了下w3c的parseInt定義: Crazy

clipboard.png
再看map,

clipboard.png
parseInt就是回調函數,map會傳給parseInt三個參數,parseInt只識別前兩個,
那也就是獲得了

function parseInt1 (item, index) {
  return parseInt(item,index)
}

獲得 parseInt(1,0) parseInt(2,1) parseInt(3,2)

parseInt(1,0),parseInt定義radix不傳或者0,按10進制,也就獲得了1
parseInt(2,1),parseInt又定義第二個參數 radix 位於 2-36(除了0),不然返回NaN,因此獲得NaN

parseInt(3,2),這個聽說(網上)是 3不是 2的合法進制數 (只有0和1),可是我的試了試
parseInt(10,2) => 3,parseInt(20, 2) => 6,parseInt(30, 2) => NaN

,發現只要是字符首字符小於 radix都是能夠的,可是一旦首字符 >= radix,就會返回NaN

參考文章:判斷js數據類型的四種方法

相關文章
相關標籤/搜索