實現一個new的偽代碼
function _new(){
let obj = new Object();
let Con = [].shift.call(arguments);
obj.__proto__ = Con.prototype;
let result = Con.apply(obj,arguments);
return typeof result === 'object' ? result : obj
}
深拷貝和淺拷貝
// ----------------------------------------------淺拷貝
// 只是把對象的屬性和屬性值拷貝到另一個對象中
var obj1 = {
a: {
a1: { a2: 1 },
a10: { a11: 123, a111: { a1111: 123123 } }
},
b: 123,
c: "123"
}
// 方式1
function shallowClone1(o) {
let obj = {}
for (let i in o) {
obj[i] = o[i]
}
return obj
}
// 方式2
var shallowObj2 = { ...obj1 }
// 方式3
var shallowObj3 = Object.assign({}, obj1)
let shallowObj = shallowClone1(obj1);
shallowObj.a.a1 = 999
shallowObj.b = true
console.log(obj1); //第一層的沒有被改變,一層以下就被改變了
// ----------------------------------------------深拷貝
// 簡易版
function deepClone(o) {
let obj = {}
for (var i in o) {
// if(o.hasOwnProperty(i)){
if (typeof o[i] === "object") {
obj[i] = deepClone(o[i])
} else {
obj[i] = o[i]
}
// }
}
return obj
}
var myObj = {
a: {
a1: { a2: 1 },
a10: { a11: 123, a111: { a1111: 123123 } }
},
b: 123,
c: "123"
}
var deepObj1 = deepClone(myObj)
deepObj1.a.a1 = 999
deepObj1.b = false
console.log(myObj);
// 簡易版存在的問題:參數沒有做檢驗,傳入的可能是 Array、null、regExp、Date
function deepClone2(o) {
if (Object.prototype.toString.call(o) === "[object Object]") { //檢測是否為對象
let obj = {}
for (var i in o) {
if (o.hasOwnProperty(i)) {
if (typeof o[i] === "object") {
obj[i] = deepClone(o[i])
} else {
obj[i] = o[i]
}
}
}
return obj
} else {
return o
}
}
function isObject(o) {
return Object.prototype.toString.call(o) === "[object Object]" || Object.prototype.toString.call(o) === "[object Array]"
}
// 繼續升級,沒有考慮到數組,以及ES6中的map、set、weakset、weakmap
function deepClone3(o) {
if (isObject(o)) {//檢測是否為對象或者數組
let obj = Array.isArray(o) ? [] : {}
for (let i in o) {
if (isObject(o[i])) {
obj[i] = deepClone(o[i])
} else {
obj[i] = o[i]
}
}
return obj
} else {
return o
}
}
// 有可能碰到循環引用問題 var a = {}; a.a = a; clone(a);//會造成一個死循環
// 循環檢測
// 繼續升級
function deepClone4(o, hash = new map()) {
if (!isObject(o)) return o//檢測是否為對象或者數組
if (hash.has(o)) return hash.get(o)
let obj = Array.isArray(o) ? [] : {}
hash.set(o, obj)
for (let i in o) {
if (isObject(o[i])) {
obj[i] = deepClone4(o[i], hash)
} else {
obj[i] = o[i]
}
}
return obj
}
// 遞歸易出現爆棧問題
// 將遞歸改為循環,就不會出現爆棧問題了
var a1 = { a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' };
var b1 = { b: { c: { d: 1 } } }
function cloneLoop(x) {
const root = {};
// 棧
const loopList = [ //->[]->[{parent:{a:1,b:2},key:c,data:{ c1: 3, c2: { c21: 4, c22: 5 } }}]
{
parent: root,
key: undefined,
data: x,
}
];
while (loopList.length) {
// 深度優先
const node = loopList.pop();
const parent = node.parent; //{} //{a:1,b:2}
const key = node.key; //undefined //c
const data = node.data; //{ a: 1, b: 2, c: { c1: 3, c2: { c21: 4, c22: 5 } }, d: 'asd' } //{ c1: 3, c2: { c21: 4, c22: 5 } }}
// 初始化賦值目標,key 為 undefined 則拷貝到父元素,否則拷貝到子元素
let res = parent; //{}->{a:1,b:2,d:'asd'} //{a:1,b:2}->{}
if (typeof key !== 'undefined') {
res = parent[key] = {};
}
for (let k in data) {
if (data.hasOwnProperty(k)) {
if (typeof data[k] === 'object') {
// 下一次循環
loopList.push({
parent: res,
key: k,
data: data[k],
})
} else {
res[k] = data[k];
}
}
}
}
return root
}
function deepClone5(o) {
let result = {}
let loopList = [
{
parent: result,
key: undefined,
data: o
}
]
while (loopList.length) {
let node = loopList.pop()
let { parent, key, data } = node
let anoPar = parent
if (typeof key !== 'undefined') {
anoPar = parent[key] = {}
}
for (let i in data) {
if (typeof data[i] === 'object') {
loopList.push({
parent: anoPar,
key: i,
data: data[i]
})
} else {
anoPar[i] = data[i]
}
}
}
return result
}
let cloneA1 = deepClone5(a1)
cloneA1.c.c2.c22 = 5555555
console.log(a1);
console.log(cloneA1);
// ------------------------------------------JSON.stringify()實現深拷貝
function cloneJson(o) {
return JSON.parse(JSON.stringify(o))
}
// let obj = { a: { c: 1 }, b: {} };
// obj.b = obj;
// console.log(JSON.parse(JSON.stringify(obj))) // 報錯 // Converting circular structure to JSON
防抖節流
// ---------------------------------------------------------防抖函數
function debounce(func, delay) {
let timeout
return function () {
let arg = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func(arg)
}, delay);
}
}
// ---------------------------------------------------------立即執行防抖函數
function debounce2(fn, delay) {
let timer
return function () {
let args = arguments
if (timer) clearTimeout(timer)
let callNow = !timer
timer = setTimeout(() => {
timer = null
}, delay);
if (callNow) { fn(args) }
}
}
// ---------------------------------------------------------立即執行防抖函數+普通防抖
function debounce3(fn, delay, immediate) {
let timer
return function () {
let args = arguments
let _this = this
if (timer) clearTimeout(timer)
if (immediate) {
let callNow = !timer
timer = setTimeout(() => {
timer = null
}, delay);
if (callNow) { fn.apply(_this, args) }
} else {
timeout = setTimeout(() => {
func.apply(_this, arguments)
}, delay);
}
}
}
// ---------------------------------------------------------節流 ,時間戳版
function throttle(fn, wait) {
let previous = 0
return function () {
let now = Date.now()
let _this = this
let args = arguments
if (now - previous > wait) {
fn.apply(_this, arguments)
previous = now
}
}
}
// ---------------------------------------------------------節流 ,定時器版
function throttle2(fn, wait) {
let timer
return function () {
let _this = this
let args = arguments
if (!timer) {
timer = setTimeout(() => {
timer = null
fn.apply(_this, arguments)
}, wait);
}
}
}
call
Function.prototype.myCall = function (context) {
// 先判斷調用myCall是不是一個函數
// 這里的this就是調用myCall的
if (typeof this !== 'function') {
throw new TypeError("Not a Function")
}
// 不傳參數默認為window
context = context || window
// 保存this
context.fn = this
// 保存參數
let args = Array.from(arguments).slice(1) //Array.from 把偽數組對象轉為數組
// 調用函數
let result = context.fn(...args)
delete context.fn
return result
}
apply
Function.prototype.myApply = function (context) {
// 判斷this是不是函數
if (typeof this !== "function") {
throw new TypeError("Not a Function")
}
let result
// 默認是window
context = context || window
// 保存this
context.fn = this
// 是否傳參
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind
Function.prototype.myBind = function(context){
// 判斷是否是一個函數
if(typeof this !== "function") {
throw new TypeError("Not a Function")
}
// 保存調用bind的函數
const _this = this
// 保存參數
const args = Array.prototype.slice.call(arguments,1)
// 返回一個函數
return function F () {
// 判斷是不是new出來的
if(this instanceof F) {
// 如果是new出來的
// 返回一個空對象,且使創建出來的實例的__proto__指向_this的prototype,且完成函數柯里化
return new _this(...args,...arguments)
}else{
// 如果不是new出來的改變this指向,且完成函數柯里化
return _this.apply(context,args.concat(...arguments))
}
}
}
原型繼承
var person = {
friends: ["a", "b", "c", "d"]
}
var p1 = Object.create(person)
p1.friends.push("aaa")//缺點:子類的實例共享了父類構造函數的引用屬性
console.log(p1);
console.log(person);//缺點:子類的實例共享了父類構造函數的引用屬性
組合繼承
function Father(name) {
this.name = name
this.hobby = ["籃球", "足球", "乒乓球"]
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = new Father()
Son.prototype.constructor = Son
var s = new Son("ming", 20)
寄生組合繼承
function Father(name) {
this.name = name
this.hobby = ["籃球", "足球", "乒乓球"]
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son
var s2 = new Son("ming", 18)