手写系列
new
思路:最终返回一个对象(或函数)
1、获取函数fn () //var Contructor = [].shift.call(arguments)
2、创建空对象,设置 对象._ proto_ = fn.prototype
3、执行函数,获取返回值
4、判断返回值是否为对象或函数,是则返回,否则返回创建的对象
function myNew(){
var obj = new Object()
var Contructor = [].shift.call(arguments)
if (Object.prototype.toString.call(Contructor) !== '[object Function]'){
throw new TypeError(Contructor + 'is not a function')
}
obj.__proto__ = Contructor.prototype
var ret = Contructor.apply(obj, arguments)
return (typeof ret === 'object' || typeof ret === "function")?ret || obj :obj
}
//函数式
function myNew(fn){
if (Object.prototype.toString.call(fn) !== '[object Function]'){
throw new TypeError(fn + 'is not a function')
}
return function(){
var obj = {
__proto__ = fn.prototype
}
fn.call(obj,...arguments)
return obj
}
}
function Otaku (name,age){
this.name = name
this.age = age
this.habit = "game"
return "111"
}
Otaku.prototype.p = "p"
Otaku.prototype.say = function(){
return 'hello'
}
var p = myNew(Otaku,"kin",18)
console.log(p.name)
console.log(p.age)
console.log(p.habit)
console.log(p.p)
console.log(p.say())
bind
思路:最终返回一个函数
1、获取原函数,获得第一个参数(用于bind的obj)
2、在返回函数体获得参数并执行函数,使用call/apply
//无参数情况下
Function.prototype.bind2 = function (context){
var self = this
return function (){
return self.apply(context)
}
}
//有参数情况下,在bind的时候传入一部分,在调用的时候传入一部分
Function.prototype.bind2 = function (context){
var self = this
//获取绑定时的参数
var args = Array.prototype.slice.call(arguments,1)
return function(){
//获取调用时传入的参数
var bindArgs = Array.prototype.slice.call(arguments)
return self.apply(context, args.concat(bindArgs))
}
}
//支持new
Function.prototype.bind2 = function(context){
var self = this
var args = Array.prototype.slice.call(arguments,1)
var fBound = function(){
var bindArgs = Array.prototype.slice.call(arguments)
return self.apply(this instanceof fBound ? this : context,args.concat
(bindArgs))
}
fBound.prototype = this.prototype
return fBound
}
//支持new 使用Object.create 挂属性(最终版本)
Function.prototype.bind2 = function (context) {
var self = this
var args = Array.prototype.slice.call(arguments, 1)
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments)
return self.apply(this instanceof fBound ? this : context, args.concat
(bindArgs))
}
fBound.prototype = Object.create(this.prototype)
return fBound
}
var foo = {value:1}
function bar(name,age){
console.log(this.value) // undefined
this.name = name
this.age = age
console.log(name) //"name"
console.log(age) //"age"
}
var bin = bar.bind2(foo,"name")
var b = new bin("age")
console.log(b)//function
console.log(b.age) // "age"
call
思路:执行一个函数,this指向传入的第一个参数
1、context 为null 时 ,this指向window
2、有参数情况,获取到函数,挂到对象上,执行,删除对象上的函数属性
3、有返回值情况下,获取返回值返回
// 无参数情况下,获取到函数,挂到对象上,执行,删除对象上的函数属性
Function.prototype.call2 =function(context){
context.fn = this
context.fn()
delete context.fn
}
// 有参数情况下,获取到函数,挂到对象上,执行,删除对象上的函数属性
Function.prototype.call2 = function (context) {
context.fn = this
// conetxt.fn()
//ES6写法
// var args = []
// for (var i = 1;i<arguments.length;i++){//此处参数从1开始
// args.push('arguments['+i+']')
// }
// eval('context.fn('+args+')')
let args = [...arguments].slice(1)
context.fn(...args)
delete context.fn
}
//context 为null 时 ,this指向window
//有参数情况,获取到函数,挂到对象上,执行,删除对象上的函数属性
//有返回值情况下,获取返回值返回
Function.prototype.call2 = function (context) {
context = context || window // 浏览器下为window,对象为null 时,this为全局
context.fn = this
let args = [...arguments].slice(1)
const ret = context.fn(...args)
delete context.fn
return ret
}
var value = 2
var foo = {
value: 1
}
function bar(name) {
console.log(this.value)
console.log(name)
return 1
}
console.log(bar.call2(foo,"a"))
apply
思路:与call 相似,只是参数为数组或5无
1、context 为null 时 ,this指向window
2、有参数情况,获取到函数,挂到对象上,执行,删除对象上的函数属性
3、有返回值情况下,获取返回值返回
//ES3写法
Function.prototype.apply2 = function(context,arr){
context = context || window
context.fn = this
var ret;
if (!arr){
ret = context.fn()
}
else {
var args = []
for(var i = 0;i<arr.length;i++){
args.push('arr['+i+']')
}
ret = eval('context.fn('+args+')')
}
delete context.fn
return ret
}
Function.prototype.apply2 = function (context) {
context = context || window
context.fn = this
let ret
if (arguments[1]){
ret = context.fn(...arguments[1])
}
else {
ret = context.fn()
}
delete context.fn
return ret
}
var value = 2
var foo = {
value: 1
}
function bar(name,age) {
console.log(this.value)
console.log(age)
console.log(name)
return 1
}
console.log(bar.apply2(foo, ["a","age"]))
JSON(JSONStringify/JSONParse)
1、JSONStringify
思路
1、除了对象和数组,其他字符串包装后返回
2、对象和数组,拿到值继续递归
3、返回值判断用[] 还是 {}
function jsonStringify(obj) {
let type = typeof obj;
if (type !== "object" || obj === null) {
// if (/string|undefined|function/.test(type)) {
// obj = '"' + obj + '"';
// }
return String(obj);
} else {
let json = [],
arr = (obj.constructor === Array);
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 = myJsonStringify(v);
}
json.push((arr ? "" : '"' + k + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
}
}
JSON.parse(jsonStringify(["a",undefined]))
2、JSONParse
//有两种
// eval 和 Function
function jsonParse(opt){
return eval('(' + opt + ')')
}
function jsonParse (opt){
return (new Function('return '+opt))()
}
var jsonStr = JSON.stringify({a:"a"})
console.log(jsonParse(jsonStr))
deepClone
//循环引用
//解决循环引用问题,我们可以额外开辟一个存储空间,
//来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,
//先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,
//如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
if (Object.prototype.hasOwnProperty.call(target,key)){
cloneTarget[key] = clone(target[key], map);
}
}
return cloneTarget;
} else {
return target;
}
};
const obj = {
test: {
a: 2
},
arr: [],
fn: function () {
console.log("clone function");
}
};
//故意设置循环引用造成Maximum call stack size
obj.obj = obj;
console.log(clone(obj));
instanceof
function new_instanceof (leftValue,rightValue){
let leftValue = leftValue.__proto__
let rightValue = rightValue.prototype
while(true){
if (leftValue === rightValue) return true
if (leftValue === null) return false //Object.prototype.__proto__ === null
leftValue = leftValue.__proto__
}
}
Object.create
// 思路:将传入的对象作为原型
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
reduce
Array.prototype.reduce2 = function(){
var ary = this
var {length} = ary
if (arguments.length === 0) {
throw new TypeError('undefined is not a function')
}
if (typeof arguments[0] !== "function") {
throw new TypeError(arguments[0] + 'is not a function')
}
if (length === 0 && arguments.length === 1){
throw new TypeError('Reduce of empty array with no initial value')
}
var callback = arguments[0]
var startIndex = arguments.length >= 2?0:1
var value = startIndex === 0?arguments[1]:ary[0]
for(var i = startIndex;i<length;i++){
value = callback(value,ary[i],i,ary)
}
return value
}
Array.prototype.reduceRight2 = function(){
var ary = this
var {length} = ary
if (arguments.length === 0) {
throw new TypeError('undefined is not a function')
}
if (typeof arguments[0] !== "function") {
throw new TypeError(arguments[0] + 'is not a function')
}
if (length === 0 && arguments.length === 1) {
throw new TypeError('Reduce of empty array with no initial value')
}
var callback = arguments[0]
var startIndex = arguments.length >= 2? length - 1:length - 2
var value = startIndex === length - 1 ?arguments[1]: ary[length - 1]
for (var i = startIndex;i>=0;i--){
value = callback(value,ary[i],i,ary)
}
return value
}
map
Array.prototype.map2 = function(){
var ary = this
var res = new Array(ary.length)
var [fn,self] = Array.prototype.slice.call(arguments)
if (typeof fn !== "function"){
throw new TypeError(fn + 'is not a function')
}
for (var i = 0;i<ary.length;i++){
if (i in ary){
res[i] = fn.call(self,ary[i],i,ary)
}
}
return res
}
function mapFromReduce(fn,self){
return (ary)=>{
if (typeof fn !== 'function') {
throw new TypeError(fn + 'is not a function');
}
if (!Array.isArray(ary)) {
throw new TypeError('list must be a Array');
}
if (ary.length === 0) return [];
var res = new Array(ary.length)
return ary.reduce((acc,cur,i)=>{
if (i in ary){
acc[i] = fn.call(self, cur, i, ary)
}
return acc
}, res)
}
}
var res = mapFromReduce(x=>x+1)([1,2,3])
// var res = [1,2,3].map(function(item){
// console.log(this.a)
// return item
// },{a:"a"})
console.log(res)
节流 throttle
var count = 1
var container = document.getElementById("container")
function getUserAction(e) {
// console.log(e)
container.innerHTML = count++
}
container.onmousemove = throttle(getUserAction,1000)
// 时间戳
function throttle(fn,wait){
var context,args
var previous = 0
return function(){
var now = +new Date()
context = this
args = arguments
if (now - previous > wait){
fn.apply(context,args)
previous = +new Date()
}
}
}
// 定时器
function throttle(fn, wait) {
var context, args
var timeout
return function(){
if (!timeout){
context = this
args = arguments
timeout = setTimeout(()=>{
fn.apply(context,args)
timeout = null
},wait)
}
}
}
//二者结合 马上执行,还有离开后到时间会继续执行一次
function throttle (fn,wait){
var timeout,context,args
var previous = 0
var throttle = function(){
context = this
args = arguments
var now = +new Date()
var remaining = wait - (now - previous)
if (remaining<0 || remaining>wait){//立即执行
if (timeout){
clearTimeout(timeout)
timeout = null
}
previous = now
fn.apply(context, args)
}
else if(!timeout){//延时执行
timeout = setTimeout(() => {
previous = +new Date()
timeout = null
fn.apply(context,args)
}, remaining);
}
}
throttle.cancle = function(){
clearTimeout(timeout)
timeout = null
previous = 0
}
return throttle
}
去抖 debounce
// function debounce(func) {
// var t;
// return function () {
// cancelAnimationFrame(t)
// t = requestAnimationFrame(func);
// }
// }
var count = 1
var container = document.getElementById("container")
function getUserAction(e){
// console.log(e)
container.innerHTML = count++
}
// container.onmousemove = getUserAction
//初版
function debounce(fn,wait){
var timeout
return function(){
clearTimeout(timeout)
timeout = setTimeout(fn,wait)
}
}
// this指向
function debounce(fn, wait) {
var timeout
return function () {
var context = this
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(context)
}, wait)
}
}
// this指向、event对象(参数)
function debounce(fn, wait) {
var timeout
return function () {
var context = this
var args = arguments
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
//添加是否立即执行,后等待时间(添加返回值)
// 此时注意一点,就是 getUserAction 函数可能是有返回值的,
// 所以我们也要返回函数的执行结果,但是当 immediate 为 false 的时候,
// 因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,
// 最后再 return 的时候,值将会一直是 undefined,
// 所以我们只在 immediate 为 true 的时候返回函数的执行结果。
function debounce(fn, wait,immediate) {
var timeout
var res
return function () {
var context = this
var args = arguments
timeout && clearTimeout(timeout)
if (immediate){
var runNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (runNow) res = fn.apply(context,args)
}
else {
timeout = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
return res
}
}
//立即执行
container.onmousemove = debounce(getUserAction, 500, true)
// 添加cancle方法 函数返回一个方法,方法中带cancle函数
function debounce(fn, wait, immediate) {
var timeout
var res
var debounce = function () {
var context = this
var args = arguments
timeout && clearTimeout(timeout)
if (immediate) {
var runNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (runNow) res = fn.apply(context, args)
}
else {
timeout = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
return res
}
debounce.cancle = function(){
clearTimeout(timeout)
timeout = null
}
return debounce
}
clone Buffer
from lodash
const allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;
function cloneBuffer(buffer, isDeep) {
if (isDeep) {
return buffer.slice();
}
var length = buffer.length,
result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
buffer.copy(result);
return result;
}
clone RegExp
from lodash
由于JSON.parse无法深拷贝 正则对象,所以自己实现
核心是lastIndex要跟着改变
如果正则表达式设置了全局标志,test() 的执行会改变正则表达式 lastIndex属性。连续的执行test()方法,后续的执行将会从 lastIndex 处开始匹配字符串,(exec() 同样改变正则本身的 lastIndex属性值).
var regex = /foo/g;
// regex.lastIndex is at 0
const a = regex.test('foo'); // true
console.log(a) //true
// regex.lastIndex is now at 3
const b = regex.test('foo'); // false
console.log(b) //false
深拷贝一份实现与原regex一样的功能
var regex = /foo/g
const a = regex.test('foo'); // true
console.log(a)
var regex2 = cloneRegExp(regex)
const b = regex2.test('foo'); // false
console.log(b) // false
function cloneRegExp(regexp){
// const result = new regexp.constructor(regexp.source, regexp.flags)
const result = new regexp.constructor(regexp.source, /\w*$/.exec(regexp))
result.lastIndex = regexp.lastIndex
return result
}