
數組去重
(一維)數組去重最原始的方法就是使用雙層循環,分別循環原始數組和新建數組;或者咱們可使用indexOf
來簡化內層的循環;或者能夠將原始數組排序完再來去重,這樣會減小一個循環,只須要比較先後兩個數便可;固然咱們可使用ES5
,ES6
的方法來簡化去重的寫法,好比咱們可使用filter
來簡化內層循環,或者使用Set
、Map
、擴展運算符這些用起來更簡單的方法,可是效率上應該不會比原始方法好。二維數組的去重能夠在上面方法的基礎上再判斷元素是否是數組,若是是的話,就進行遞歸處理。javascript
雙層循環
java
var array = [1, 1, '1', '1'];
function unique(array) { var res = []; for (var i = 0, arrayLen = array.length; i < arrayLen; i++) { for (var j = 0, resLen = res.length; j < resLen; j++ ) { if (array[i] === res[j]) { break; } } if (j === resLen) { res.push(array[i]) } } return res; }前端
複製代碼console.log(unique(array)); // [1, "1"] 複製代碼複製代碼
利用indexOf
var array = [1, 1, '1'];
function unique(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res;
}
console.log(unique(array));
複製代碼複製代碼
排序後去重
var array = [1, 1, '1'];
function unique(array) {
var res = [];
var sortedArray = array.concat().sort();
var seen;
for (var i = 0, len = sortedArray.length; i < len; i++) {
// 若是是第一個元素或者相鄰的元素不相同
if (!i || seen !== sortedArray[i]) {
res.push(sortedArray[i])
}
seen = sortedArray[i];
}
return res;
}
console.log(unique(array));
複製代碼複製代碼
filter
filter
能夠用來簡化外層循環node
使用indexOf:jquery
github
var array = [1, 2, 1, 1, '1'];
function unique(array) { var res = array.filter(function(item, index, array){ return array.indexOf(item) === index; }) return res; }git
複製代碼console.log(unique(array)); 複製代碼複製代碼
排序去重:面試
var array = [1, 2, 1, 1, '1'];
function unique(array) {
return array.concat().sort().filter(function(item, index, array){
return !index || item !== array[index - 1]
})
}
console.log(unique(array));
複製代碼複製代碼
ES6方法
Set:json
var array = [1, 2, 1, 1, '1'];
function unique(array) { return Array.from(new Set(array)); }c#
複製代碼console.log(unique(array)); // [1, 2, "1"] 複製代碼複製代碼
再簡化下
function unique(array) {
return [...new Set(array)];
}
//或者
var unique = (a) => [...new Set(a)]
複製代碼複製代碼
Map:
function unique (arr) {
const seen = new Map()
return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}
複製代碼複製代碼
類型判斷
類型判斷須要注意如下幾點
-
typeof
對六個基本數據類型Undefined
、Null
、Boolean
、Number
、String
、Object
(大寫)返回的結果是undefined
、object
、boolean
、number
、string
、object
(小寫),能夠看到Null
和Object
類型都返回了object
字符串;typeof
卻能檢測出函數類型;綜上,typeof
能檢測出六種類型,可是不能檢測出null
類型和Object
下細分的類型,如Array
,Function
,Date
,RegExp
,Error
等。 -
Object.prototype.toString
的做用很是強大,它能檢測出基本數據類型以及Object
下的細分類型,甚至像Math
,JSON
,arguments
它都能檢測出它們的具體類型,它返回結果形式例如[object Number]
(注意最後的數據類型是大寫).因此,Object.prototype.toString
基本上能檢測出全部的類型了,只不過有時須要考慮到兼容性低版本瀏覽器的問題。
通用API
// 該類型判斷函數能夠判斷六種基本數據類型以及Boolean Number String Function Array Date RegExp Object Error,
// 其餘類型由於遇到類型判斷的狀況較少因此都會返回object,不在進行詳細的判斷
// 好比ES6新增的Symbol,Map,Set等類型
var classtype = {};
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item) { classtype["[object " + item + "]"] = item.toLowerCase(); })
function type(obj) { // 解決IE6中null和undefined會被Object.prototype.toString識別成[object Object] if (obj == null) { return obj + ""; }
<span class="hljs-comment">//若是是typeof後類型爲object下的細分類型(Array,Function,Date,RegExp,Error)或者是Object類型,則要利用Object.prototype.toString</span>
<span class="hljs-comment">//因爲ES6新增的Symbol,Map,Set等類型不在classtype列表中,因此使用type函數,返回的結果會是object</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">typeof</span> obj === <span class="hljs-string">"object"</span> || <span class="hljs-keyword">typeof</span> obj === <span class="hljs-string">"function"</span> ?
classtype[<span class="hljs-built_in">Object</span>.prototype.toString.call(obj)] || <span class="hljs-string">"object"</span> :
<span class="hljs-keyword">typeof</span> obj;
複製代碼
複製代碼<span class="hljs-comment">//若是是typeof後類型爲object下的細分類型(Array,Function,Date,RegExp,Error)或者是Object類型,則要利用Object.prototype.toString</span>
<span class="hljs-comment">//因爲ES6新增的Symbol,Map,Set等類型不在classtype列表中,因此使用type函數,返回的結果會是object</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">typeof</span> obj === <span class="hljs-string">"object"</span> || <span class="hljs-keyword">typeof</span> obj === <span class="hljs-string">"function"</span> ?
classtype[<span class="hljs-built_in">Object</span>.prototype.toString.call(obj)] || <span class="hljs-string">"object"</span> :
<span class="hljs-keyword">typeof</span> obj;
複製代碼} 複製代碼複製代碼
判斷空對象
判斷是否有屬性,for
循環一旦執行,就說明有屬性,此時返回false
function isEmptyObject( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
}
複製代碼console.log(isEmptyObject({})); // true console.log(isEmptyObject([])); // true console.log(isEmptyObject(null)); // true console.log(isEmptyObject(undefined)); // true console.log(isEmptyObject(1)); // true console.log(isEmptyObject('')); // true console.log(isEmptyObject(true)); // true 複製代碼複製代碼
咱們能夠看出isEmptyObject
實際上判斷的並不只僅是空對象。可是既然jQuery
是這樣寫,多是由於考慮到實際開發中 isEmptyObject
用來判斷 {} 和 {a: 1} 是足夠的吧。若是真的是隻判斷 {},徹底能夠結合上篇寫的 type
函數篩選掉不適合的狀況。
判斷Window對象
Window
對象有一個window
屬性指向自身,能夠利用這個特性來判斷是不是Window
對象
function isWindow( obj ) {
return obj != null && obj === obj.window;
}
複製代碼複製代碼
判斷數組
isArray
是數組類型內置的數據類型判斷函數,可是會有兼容性問題,一個polyfill
以下
isArray = Array.isArray || function(array){
return Object.prototype.toString.call(array) === '[object Array]';
}
複製代碼複製代碼
判斷類數組
jquery
實現的isArrayLike
,數組和類數組都會返回true
。所若是isArrayLike
返回true
,至少要知足三個條件之一:
-
是數組
-
長度爲 0 好比下面狀況,若是咱們去掉length === 0 這個判斷,就會打印
false
,然而咱們都知道arguments
是一個類數組對象,這裏是應該返回true
的function a(){ console.log(isArrayLike(arguments)) } a(); 複製代碼複製代碼
-
lengths
屬性是大於 0 的數字類型,而且obj[length - 1]
必須存在(考慮到arr = [,,3]的狀況)
function isArrayLike(obj) {
<span class="hljs-comment">// obj 必須有 length屬性</span>
<span class="hljs-keyword">var</span> length = !!obj && <span class="hljs-string">"length"</span> <span class="hljs-keyword">in</span> obj && obj.length;
<span class="hljs-keyword">var</span> typeRes = type(obj);
<span class="hljs-comment">// 排除掉函數和 Window 對象</span>
<span class="hljs-keyword">if</span> (typeRes === <span class="hljs-string">"function"</span> || isWindow(obj)) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
<span class="hljs-keyword">return</span> typeRes === <span class="hljs-string">"array"</span> || length === <span class="hljs-number">0</span> ||
<span class="hljs-keyword">typeof</span> length === <span class="hljs-string">"number"</span> && length > <span class="hljs-number">0</span> && (length - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> obj;
複製代碼
複製代碼<span class="hljs-comment">// obj 必須有 length屬性</span>
<span class="hljs-keyword">var</span> length = !!obj && <span class="hljs-string">"length"</span> <span class="hljs-keyword">in</span> obj && obj.length;
<span class="hljs-keyword">var</span> typeRes = type(obj);
<span class="hljs-comment">// 排除掉函數和 Window 對象</span>
<span class="hljs-keyword">if</span> (typeRes === <span class="hljs-string">"function"</span> || isWindow(obj)) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
<span class="hljs-keyword">return</span> typeRes === <span class="hljs-string">"array"</span> || length === <span class="hljs-number">0</span> ||
<span class="hljs-keyword">typeof</span> length === <span class="hljs-string">"number"</span> && length > <span class="hljs-number">0</span> && (length - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> obj;
複製代碼} 複製代碼複製代碼
判斷NaN
判斷一個數是否是NaN
不能單純地使用 === 這樣來判斷, 由於NaN
不與任何數相等, 包括自身,注意在ES6
的isNaN
中只有值爲數字類型使用NaN
纔會返回true
isNaN: function(value){
return isNumber(value) && isNaN(value);
}
複製代碼複製代碼
判斷DOM元素
利用DOM
對象特有的nodeType
屬性(
isElement: function(obj){
return !!(obj && obj.nodeType === 1);
// 兩次感嘆號將值轉化爲布爾值
}
複製代碼複製代碼
判斷arguments對象
低版本的瀏覽器中argument
對象經過Object.prototype.toString
判斷後返回的是[object Object]
,因此須要兼容
isArguments: function(obj){
return Object.prototype.toString.call(obj) === '[object Arguments]' || (obj != null && Object.hasOwnProperty.call(obj, 'callee'));
}
複製代碼複製代碼
深淺拷貝
若是是數組,實現淺拷貝,比能夠slice
,concat``返回一個新數組的特性來實現;實現深拷貝,能夠利用JSON.parse
和JSON.stringify
來實現,可是有一個問題,不能拷貝函數(此時拷貝後返回的數組爲null)。上面的方法都屬於技巧,下面考慮怎麼實現一個對象或者數組的深淺拷貝
淺拷貝
思路很簡單,遍歷對象,而後把屬性和屬性值都放在一個新的對象就OK了
var shallowCopy = function(obj) {
// 只拷貝對象
if (typeof obj !== 'object') return;
// 根據obj的類型判斷是新建一個數組仍是對象
var newObj = obj instanceof Array ? [] : {};
// 遍歷obj,而且判斷是obj的屬性才拷貝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
複製代碼複製代碼
深拷貝
思路也很簡單,就是在拷貝的時候判斷一下屬性值的類型,若是是對象,就遞歸調用深淺拷貝函數就ok了
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
複製代碼複製代碼
扁平化
遞歸
循環數組元素,若是仍是一個數組,就遞歸調用該方法
// 方法 1
var arr = [1, [2, [3, 4]]];
function flatten(arr) { var result = []; for (var i = 0, len = arr.length; i < len; i++) { if (Array.isArray(arr[i])) { result = result.concat(flatten(arr[i])) } else { result.push(arr[i]) } } return result; }
複製代碼console.log(flatten(arr)) 複製代碼複製代碼
toString()
若是數組的元素都是數字,可使用該方法
// 方法2
var arr = [1, [2, [3, 4]]];
function flatten(arr) { return arr.toString().split(',').map(function(item){ return +item // +會使字符串發生類型轉換 }) }
複製代碼console.log(flatten(arr)) 複製代碼複製代碼
reduce()
// 方法3
var arr = [1, [2, [3, 4]]];
function flatten(arr) {
return arr.reduce(function(prev, next){
return prev.concat(Array.isArray(next) ? flatten(next) : next)
}, [])
}
console.log(flatten(arr))
複製代碼複製代碼
...
// 扁平化一維數組
var arr = [1, [2, [3, 4]]];
console.log([].concat(...arr)); // [1, 2, [3, 4]]
// 能夠扁平化多維數組
var arr = [1, [2, [3, 4]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr))
複製代碼複製代碼
防抖與節流
防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null)
{
clearTimeout(timeout);
}
timeout = setTimeout(fn, wait);
}
}
// 處理函數
function handle() {
console.log(Math.random());
}
// 滾動事件
window.addEventListener('scroll', debounce(handle, 1000));
複製代碼複製代碼
節流
利用時間戳實現
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
複製代碼複製代碼
利用定時器實現
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
複製代碼複製代碼
模擬new
new
產生的實例能夠訪問Constructor
裏的屬性,也能夠訪問到Constructor.prototype
中的屬性,前者能夠經過apply
來實現,後者能夠經過將實例的proto
屬性指向構造函數的prototype
來實現- 咱們還須要判斷返回的值是否是一個對象,若是是一個對象,咱們就返回這個對象,若是沒有,咱們該返回什麼就返回什麼
function New(){
var obj=new Object();
//取出第一個參數,就是咱們要傳入的構造函數;此外由於shift會修改原數組,因此arguments會被去除第一個參數
Constructor=[].shift.call(arguments);
//將obj的原型指向構造函數,這樣obj就能夠訪問到構造函數原型中的屬性
obj._proto_=Constructor.prototype;
//使用apply改變構造函數this的指向到新建的對象,這樣obj就能夠訪問到構造函數中的屬性
var ret=Constructor.apply(obj,arguments);
//要返回obj
return typeof ret === 'object' ? ret:obj;
}
複製代碼複製代碼
function Otaku(name,age){
this.name=name;
this.age=age;
this.habit='Games'
}
Otaku.prototype.sayYourName=function(){ console.log("I am" + this.name); }
var person=objectFactory(Otaku,'Kevin','18')
複製代碼console.log(person.name)//Kevin console.log(person.habit)//Games console.log(person.strength)//60 複製代碼複製代碼
模擬Call
call()
方法在使用一個指定的this值和若干個指定的參數值的前提下調用某個函數或方法- 模擬的步驟是:將函數設爲對象的屬性—>執行該函數—>刪除該函數
this
參數能夠傳null
,當爲null
的時候,視爲指向window
- 函數是能夠有返回值的
簡單版
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
複製代碼複製代碼
完善版
Function.prototype.call2 = function(context) {
var context=context||window
context.fn = this;
let args = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn;
return result;
}
let foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
//表示bar函數的執行環境是foo,即bar函數裏面的this表明foo,this.value至關於foo.value,而後給bar函數傳遞兩個參數
bar.call2(foo, 'black', '18') // black 18 1
複製代碼複製代碼
模擬apply
apply()
的實現和call()
相似,只是參數形式不一樣
Function.prototype.apply2 = function(context = window) {
context.fn = this
let result;
// 判斷是否有第二個參數
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
複製代碼複製代碼
模擬bind
Function.prototype.bind2=function(context){
var self=thisl
var args=Array.prototype.slice.call(arguments,1);
<span class="hljs-keyword">var</span> fNOP=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{};
<span class="hljs-keyword">var</span> fBound=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
<span class="hljs-keyword">var</span> bindArgs=<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>);
<span class="hljs-keyword">return</span> self.apply(<span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> fNOP ? <span class="hljs-keyword">this</span> : context, args.concat(bindAt))
}
複製代碼
複製代碼<span class="hljs-keyword">var</span> fNOP=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{};
<span class="hljs-keyword">var</span> fBound=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
<span class="hljs-keyword">var</span> bindArgs=<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>);
<span class="hljs-keyword">return</span> self.apply(<span class="hljs-keyword">this</span> <span class="hljs-keyword">instanceof</span> fNOP ? <span class="hljs-keyword">this</span> : context, args.concat(bindAt))
}
複製代碼} 複製代碼複製代碼
模擬instanceof
function instanceOf(left,right) {
let proto = left.__proto__;
let prototype = right.prototype
while(true) {
if(proto === null) return false
if(proto === prototype) return true
proto = proto.__proto__;
}
}
複製代碼複製代碼
模擬JSON.stringify
JSON.stringify(value[, replacer [, space]])
-
Boolean | Number| String
類型會自動轉換成對應的原始值。 -
undefined
、任意函數以及symbol
,會被忽略(出如今非數組對象的屬性值中時),或者被轉換成null
(出如今數組中時)。 -
不可枚舉的屬性會被忽略
-
若是一個對象的屬性值經過某種間接的方式指回該對象自己,即循環引用,屬性也會被忽略。
function jsonStringify(obj) {
let type = typeof obj;
if (type !== "object") {
if (/string|undefined|function/.test(type)) {
obj = '"' + obj + '"';
}
return String(obj);
} else {
let json = []
let arr = Array.isArray(obj)
for (let k in obj) {
let v = obj[k];
let type = typeof v;
if (/string|undefined|function/.test(type)) {
v = '"' + v + '"';
} else if (type === "object") {
v = jsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"
複製代碼複製代碼
參考資料: