改變函數的執行上下文中的this指向,但不執行該函數(位於Function構造函數的原型對象上的方法)javascript
Function.prototype.myBind = function (target) { if (typeof this !== 'function') { throw Error('myBind is not a function') } var that = this var args1 = [...arguments].slice(1) var func = function () { var args2 = [..arguments].slice(1) return that.apply(target || window, args1.concat(args2)) } return func } Function.prototype.myCall = function (context=window) { if (typeof this !== 'function') { throw Error('myBind is not a function') } context.fn = this var args = [...arguments].slice(1) var result = context.fn(..args) delete context.fn return result } Function.prototype.myApply = function (context=window) { if (typeof this !== 'function') { throw Error('myApply is not a function') } context.fn = this var result if (argument[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result }
function cloneDeep (target) { function checkType(target) { return Object.prototype.toString.call(target).slice(8, -1) } var result, checkedType = checkType(target) if (checkedType === 'Array') { result = [] } else if (checkedType === 'Object') { result = {} } else { return target } //遞歸遍歷對象或數組中的屬性值或元素爲原始值爲止 for (var key in target) { if ( checkType(target[key]) === 'Array' || checkType(target[key]) === 'Object') { result[key] = cloneDeep(target[key]) } else { result[key] = target[key] } } return result }
思路:java
Array.prototype.flat
分析:首先找規律,舉例如3的階乘等於3*2*1,也就是等於n*n-1*n-2的階乘,也就是等於3*2*1的階乘,計算到1的階乘以後,整個計算過程才結束。分析到很容易想到經過遞歸來實現這個數的階乘,由於第一,這個計算過程有規律可循,第二它有最終中止計算的出口,也就是當計算到1的時候就中止運算,如下經過遞歸來實現ajax
function factorial (num) { if (num < 0) { throw new Error('負數沒有階乘') } if (num === 1 || num === 0) { return 1 } return num * factorial(num-1) } factorial(3) //6
分析:按照上述階乘的分析過程分析,這裏不贅述編程
function fibonacci (n) { //此方法應使用尾遞歸法進行優化,這裏不做優化,簡單實現 if ( n <= 1 ) {return 1}; return fibonacci(n - 1) + fibonacci(n - 2);}
分析:首先咱們要知道英文的字節長度是1,而中文的字節長度是2,可是如何判斷當前字符位是漢字仍是英文呢,經過charCodeAt來判斷當前字符位的unicode編碼是否大於255,如何大於255則是漢字,那就給字符串的字節長度加2,若是小於255則是英文,就給字符串的字節長度加1,如下按照這個思路實現數組
function countBytesLength(str){ var length = 0 //首先遍歷傳入的字符串 for(var i = 0; i < str.length; i++) { if (str[i].charCodeAt(i) > 255) { length += 2 } else { length++ } } return length } var str = 'DBCDouble陳' countBytesLength(str) //11
分析:要判斷傳入的值是不是"is not a number"(isNaN全拼),首先進行一個數字的隱式類型轉換,經過Number包裝類來實現Number(x),再判斷Numberz(x)的返回值是不是NaN,若是是的話再與NaN進行比對,可是因爲NaN雖然是number類型的,可是是不能進行比較的,因此咱們先將Number(x)返回的結果變成字符串形式,再去判斷,實現以下app
function isNaN(num) { var ret = Number(num) ret += '' if (ret === 'NaN') { return true } return false } isNaN('123abc') // true
分析:首先push函數是位於Array構造函數的原型對象上的方法,因此要在Array.prototype上去定義,而後再分析push函數的做用是往數組的末尾添加元素,能夠添加任意個數的元素,而且最終返回數組的長度,實現代碼以下async
Array.prototype.push = function () { for (var i = 0; i< arguments.length; i++) { this[this.length] = arguments[i] } return this.length }
分析:首先typeof是位於window對象上的全局方法,因此咱們定義完成以後要將其掛載到window上,其次要實現識別全部數據類型包括:基本數據類型和複雜數據類型(引用數據類型),咱們須要經過Object.prototype.toString方法去作才惟一可以最準確判斷當前值爲何數據類型,實現代碼以下函數式編程
window.typeof = function (value) { return Object.prototype.toString.call(val).slice(8, -1) }
分析:首先由於是給全部數組實例實現一個去重方法,因此一樣是在原型鏈上進行編程函數
Array.prototype.unique = function () { //這裏是利用對象鍵hash值的惟一性來去重 var obj = {} var result = [] for (var i = 0; i < this.length; i++) { if (!obj[this[i]]) { obj[this[i]] = true result.push(this[i]) } } return result } var arr = [1,2,2,3,3] arr.unique() //[1,2,3]
Array.prototype.unique = function () { //利用ES6的Array.prototype.includes var result = [] for (var i = 0; i < this.length; i++) { if (!result.includes(this[i])) { result.push(this[i]) } } return result }
Array.prototype.unique = function () { //利用ES6的Set var result = new Set(this) //生成一個類數組 return Array.from(result) //經過Array.from將類數組轉換成真正的數組 }
Array.prototype.unique = function () { //利用Array.prototype.filter返回符合條件的元素 //利用Array.prototype.indexOf返回數組中第一次出現當前元素的索引值 //該方法寫法最爲優雅,一行代碼搞定,函數式編程 return this.filter((item, index) => this.indexOf(item) === index) }
function debounce (fn, wait=300) { var timer return function () { if (timer) { clearTimeOut(timer) } timer = setTimeout({ fn.apply(this, arguments) }, wait) } }
function throttle (fn, wait=300) { var prev = +new Date() return function () { var now = +new Date() if (prev - now > 300) { fn.apply(this, arguments) prev = now } } }
function ajax (options) { options = options || {} options.url = options.url || '' options.method = options.method.toUpperCase() || 'GET' options.async = options.async || true options.data = options.data || null options.success = options.success || function () {} var xhr = null if (XMLHttpRequest) { xhr = new XMLHttpRequest() } else { xhr = new ActiveXObject('Microsoft.XMLHTTP') } xhr.open(options.url, options.method, options.async) var postData = [] for (var key in options.data) { postData.push(key + '='+ options.data[key]) } if (options.method === 'POST') { xhr.open(options.method, options.url, options.async ) xhr.send(postData) } else if (options.method === 'GET') { xhr.open(options.mehtod, options.url + postData.join('&'), options.async) xhr.send(null) } xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { options.success(xhr.responseText) } } }
//接受一個函數 //最後返回一個對象 function new (fn) { return function () { var obj = { '__proto__': fn.prototype } fn.apply(obj, arguments) return obj } }
12、經常使用六種繼承方式post
一、原型鏈繼承:子類型的原型對象爲父類型的實例對象
function Person (name, age) { this.name = name this.age = age } Person.prototype.setName = function () { console.log(this.name) } function Student (height) { this.height = height } Student.prototype = new Person() var stu = new Student('175') console.log(stu)
二、借用構造函數實現繼承:在子類的構造函數中經過call調用父類的構造函數實現繼承
function Person (name, age) { this.name = name this.age = age } Person.prototype.setName = function () { console.log(this.name) } function Student (height, age, name) { Person.call(this, age, name) this.height = height } var stu = new Studeng(175, 'cs', 24) console.log(stu)
三、原型鏈+借用構造函數的組合繼承方式:經過在子類構造函數中經過call調用父類構造函數,繼承父類的屬性並保留傳參的優勢,再經過將父類的實例做爲子類的原型對象,實現繼承
function Person (name, age) { this.name = name this.age = age } Person