js高级02

Wenn 于 2022-09-24 发布

call实现

Function.prototype.hycall = function (thisarg,...agrs) { //接受剩余参数
	var fn = this;  //隐式绑定
    thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window; //判断传入this
   thisarg.fn = fn;
   var re = thisarg.fn(...agrs);   //展开args
    delete thisarg.fn
    return re;
}
function foo () {
	console.log('1',this)
}
function sum () {
	console.log('2',this)
}
foo.hycall({})
sum.hycall({})

apply实现

Function.prototype.hyapply = function (thisarg,...argArray){
    var fn = this
    thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window;
    thisarg.fn = fn;
    args = argArray || []
    var result;
    if(!args){
        result = thisarg.fn(args)
    }else {
        result = thisarg.fn(...args)
    }
    delete thisarg.fn
    return result;
}

Function.prototype.hyapply = function (thisarg,argArray){
    var gn = this
    thisarg = (thisarg !=== null && thisarg !== undefined) ? Object(thisarg) : window;
    thisarg.fn = fn;
    var result;
    if(!argArray){
        result = thisarg.fn()
    }else{
        result = thisarg.fn(...argArray)
    }
    delete thisarg.fn
    return reuslt;
}

forEach

//forEach修改原对象值
const arr = [1,2,3]
arr.forEach(item=>item+1); 
console.log(arr);   //此时不可修改

arr.forEach((item,index,arr)=>arr[index]+=1);  //此时arr为堆内存地址,直接修改地址内的
console.log(arr); //[2,3,4]

//forEach修改复杂类型对象值
const arr = [
    {age:18},
    {age:19},
    {age:20}
]
arr.forEach(item=>{
    item.age += 1;    //此时item为堆内存地址
})
console.log(arr); //[{age:19},{age:20},{age:21}]

//forEach可以被打断吗?可以
const arr = [1,2,3]
arr.forEach(item=>{
    console.log(item); //1
    throw new Error('error message')
})   //报错打断,仅执行一次

//报错但并不打断
arr.forEach(item=>{
    try{
        console.log(item)
        throw new Error('error message')
    }catch(error) {
        console.log(error)
    }
})

bind实现

Function.prototype.hybind = function(thisarg,...argsArray){
    var fn = this;
    thisarg = (thisarg !== null && thisarg !== undefined)? Object(thisarg):window;
    
    function proxyFn(...args){
    thisarg.fn = fn
    var finalArray = [...argsArray,...args]
    var result = thisarg.fn(...finalArray)
    delete thisarg.fn
    return result
    }
    return proxyFn
}

arguments

arguments是一个类数组(array-likr)的对象,无法使用数组的方法。

常见操作

function foo(num1,num2){
    console.log(arguments); //输出传入的所有实参
    console.log(arguments.length); //输出传入实参的长度
    console.log(arguments[index]); //输出arguments对应索引值的实参
    console.log(arguments.callee); //输出该函数;函数本身
    console.log(typeof arguments.callee); //function
}
foo(2,3,4,5)
arguments转数组
function foo(num1,num2){
    //1.自己遍历
    var newArr = []
    for(let i = 0 ; i < arguments.length ; i++){
        newArr.push(arguments[i]);
    }
    console.log(newArr); //输出数组与arguments值相同
    
    //2.使用数组原型slice方法
    var newArr = Array.prototype.slice.call(arguments);
    console.log(newArr)
    
    //3.使用数组.call转换
    var newArr = [].slice.call(arguments);
    console.log(newArr)
    
    //4.使用Array.from
    var newArr = Array.from(arguments);
    
    //5.使用展开运算符
    var newArr = [...arguments];
}

Array.prototype.slice实现原理

Array.prototype.hyslice = function (start,end) {
    arguments = this
    var start = start || 0;
    var end = end || arguments.length
    var newArr = [];
    for (let i = start; i < end; i++) {
        newArr.push(arguments[i]);
    }
    return newArr;
};

箭头函数没有arguments,使用时会自动想上层作用域查找,如果到全局作用域,在浏览器端是没有arguments的,但是在node中会有arguments

在node中,会把js文件当作一个模块,这个模块会被包裹在一个函数中,当执行时,则使用call调用这个函数;.call({},...)

function foo(){
    var bar = () => {
        console.log(arguments);   //此时arguments为foo的arguments
    }
    return bar
}
var fn = foo(123)
fn(); 

箭头函数中更推荐使用剩余参数

var foo = (...arg) => {
    console.log(arg)
}
foo(1,2,3)

纯函数

  • 确定的输入,一定会产生确定的输出
  • 函数在执行过程中,不能产生副作用

在开发中尽量选择纯函数做开发

//slice由于splice对比
//slice是一个纯函数,并不会修改原来的数组
var arr = [1,2,3]
var arr2 = arr.slice(0,1);  //从第0位开始,截至到1位之前,不包含1位
console.log(arr2,arr);  // [1]   [1,2,3]

//splice会原数组进行修改
var arr3 = arr.splice(2); //原数组保留2位,其余全部分配给新数组
console.log(arr3,arr);  //[3]   [1.2]

纯函数修改变量值

var obj = {name:'xiaohuang',age:20};
function foo(info){
    return {...info,gender:'male',height:'200cm'}
}
obj = foo(obj)
console.log(obj); //{name:'xiaohuang',age:20,gender:'male',height:'200cm'}

函数的柯里化(currying)

//常见函数
function foo(x,y,z){
    return x+y+z
}
var result = foo(10,20,30); //普通函数

//将多个参数转换为单一函数处理单一参数的过程被称为柯里化
function foo(x){
    return function(y){
        return function (z){
            return x+y+z
        }
    }
}
var result = foo(10)(20)(30); //此时这个过程被称为柯里化

//简化柯里化
var foo = x => y => z => x + y +z
//等价于下面的箭头函数
var foo = x => y => z =>{
    return x + y + z
}
var foo = x =>{
    return y => {
        return z {
            return x + y + z
        }
    }
}

柯里化的优势一 单一职责原则

function sum (x,y,z){
    x += 2;
    y *= 2;
    z *= z;
    return x + y +z
}
console.log(sum(1,2,3))

//复杂柯里化
function foo(x){
    x += 2;
    return function(y){
        y *= y;
        return function (z){
            return x+y+z
        }
    }
}
console.log(foo(1)(2)(3));

柯里化的逻辑复用

function makeAdder(count,num){
    return count*count + num 
}
//如果count为一个常量或固定的数,那每次调用都需要重复写该参数

//柯里化优化
function makeAdder(count,num){
    count *= count;
    return function (num){
        return count + num
    }
}
var adder5 = makeAdder(5);
console.log(adder5(10))
var adder10 = makeAdder(10);
console.log(adder10(20))

柯里化的逻辑复用2

function log(date,type,message){
    console.log(`[${date.getHours()} : ${date.getMinutes()}][${type}][${message}]`)
}
//常见调用
log(new Date(),'DEBUG','222333')


//定制化
var log = date => type => message => {
	console.log(`[${date.getHours()} : ${date.getMinutes()}][${type}][${message}]`)
}

var nowDate = log(new Date()); //固定获取当前时间
nowDate('DEBUG')('23333');

var nowType = log(new Date())('DEBUG'); //固定获取当前时间与类型
nowType('23333');

柯里化的实现

function hyCurrying(fn){
    return function curried(...args){
        if(args.lenght >= fn.length){
            return fn.apply(this,args)
        }else{
            return function (...args2){
                return curried.apply(this,[...args,...args2])
            }
        }
    }
}

组合函数

function double(count){
    return count * 2
}
function square (count){
    return count**2
}
//普通常用
var re = square(double(count))

//组合函数
function and(m,n){
    return function(count){
        return n(m(count))
    }
}

作用域补充

with语句

JS中可以形成作用与的有全局,函数以及with,对象并不生成作用域;不建议使用

严格模式下会报错

var obj = {message:'Hello,World'}
with(obj){  //将obj作为作用域传入
    console.log(message); //'Hello,World'
}//默认应该输出undefined,但实际输出并不是

eval方法

将字符串作为js代码执行;不建议使用

var jsString = 'var message = "hello";console.log(message);'
eval(jsString); //hello

对Object属性的控制 Object.defineProperty(obj,prop,descriptor)

obj为要定义属性的对象;prop为所要定义的属性;descriptor为属性描述符

Object.getOwnPropertyDescriptor(obj,'name')  //获取单个
Object.getOwnPropertyDescriptors(obj) //获取多个

禁止对象继续添加新的属性

Object.preventExtensions(obj); //限制
Object.seal(obj); //限制对对象的删除
Object.freeze(obj); //冻结,让属性不可修改,writeable:false

创建对象的方案-工厂模式

function createPerson(name){
    var p = {}
    p.name = name
    return p
}
var p1 = createPerson('xi'),
var p2 = createPerson('bi'),
console.log(p1,p2) ; //{ name: 'xi' } { name: 'bi' }

缺点:无法获取对象的真实属性