前端进阶JS函数增强与对象增强

2023-03-09 20:00:04 浏览数 (2)

JS函数增强

函数属性

JavaScript中函数也是一个对象,那么对象中就可以有属性和方法,他有一些默认的属性

  • name 函数名
  • length 函数参数个数(ES6 ... 语法不会被算在内)
  • arguments 类似数组对象 可以i用索引来获取对象
  • rset

PS: 箭头函数不绑定 Arguments 对象

arguments 转为数组对象常见方法

普通的方法 就是将内容一个一个迭代到新数组了

代码语言:javascript复制
    let newArray = []
    // arguments
    function foo1(m, n) {
        for (var arg of arguments) {
            newArray.push(arg)
        }
        // arguments类似数组的对象(它可以通过索引来获得对象)
        console.log(newArray)
    }
    foo1(1, 2)

ES6 中的方法

  • Array.form() 传入一个可迭代对象就可以转为数组
  • 对象结构 ... 的方式来复制
代码语言:javascript复制
        // 方法2
         var newArray1 = Array.from(arguments)
        // 方法3
        var newArray = [...arguments]

rset

如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组

代码语言:javascript复制
function foo1(m, n, ...arg)
  • arguments 对象包含了传给函数的所有实参但是不是数组对象 需要转换
  • rest参数是一个真正的数组,可以进行数组的所有操作
  • arguments是早期为了方便去获取所有的参数提供的数据结构,rest参数是ES6中提供并且希望替代arguments的方案

纯函数理解和应用

副作用: 执行函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储

纯函数的理解

  • 输入 相同值的时候产生同样的输出 所以纯函数不能通过闭包的特性调用上层属性,因为会随着上层属性变化函数输出内容
  • 函数的输出和输入值以外的信息无关和设备的外部输出也无关
  • 这个函数不能有语义上可观察到的 “副作用”
纯函数辨别案例
  • slice:slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组
  • splice:splice截取数组, 会返回一个新的数组, 也会对原数组进行修改
代码语言:javascript复制
    var names = ["abc", "nba", "nbc", "cbd"]
    var newNames = names.slice(0, 2)
    var newNames1 = names.splice(0, 2)
    console.log(newNames);
    console.log(newNames1);
纯函数的优势
  • 稳定,可以放心使用
  • 保证函数的纯度 简单的实现自己的业务逻辑,和外置的各种因素依赖关系少
  • 用的时候需要保证输入的内容不被任意篡改,并且需要确定输入一定会有确定的输出

柯里化的理解和应用

函数式编程重要概念,他是一个作用于函数的高阶技术,在其他的编程语言也有使用

只传递函数部分参数来调用,让它返回一个函数去处理剩余的参数这个过程就被成为柯里化

代码语言:javascript复制
   // 普通的函数
    function foo(x, y, z) {
        console.log(x   y   z);
    }
    foo(10, 20, 30)

    // 柯里化的结果
    function kelifoo(x) {
        return function (y) {
            return function (z) {
                console.log(x   y   z);
            }
        }
    }
    kelifoo(10)(20)(30)

//箭头函数写法
 var foo2 = x => y => z => { console.log(x   y   z) }
自动柯里化函数
代码语言:javascript复制
    // 需要转化的例子
    function sum(num1, num2) {
        console.log(num1   num2);
        return num1   num2
    }
    // 自动柯里化函数
    function hyCurrying(fn) {
        // 1 继续返回一个新的函数 继续接受函数
        // 2 直接执行 fn 函数
        function curryFun(...args) {
            if (args.length >= fn.length) {
                // 执行第二种操作
                return fn.apply(this, args)
            } else {
                return function (...newArgs) {
                    return curryFun.apply(this, args.concat(newArgs))
                }
            }
        }
        return curryFun
    }
    // 对其他函数柯里化
    var sumCurry = hyCurrying(sum)
    sumCurry(10)(5)
    sumCurry(10, 5)

柯里化函数只有在某些特殊的场景才需要使用。他得性能并不高也可能引起闭包的内存泄漏所以使用的时候需要注意。

组合函数理解和应用

当我们需要嵌套调用两个函数的时候,为了方便复用,我们可以写一个组合函数

代码语言:javascript复制
var sum = pow(double(12)) 

我们可以编写一个通用的组合函数来让我们使用组合函数更加的便捷,其实思路就是很简单的将函数放入数组判断边界顺序执行

代码语言:javascript复制
    function sum(num) {
        return num * 2
    }
    function pow(num) {
        return num ** 2
    }

    function composeFn(...fns) {
        // 边界判断
        var length = fns.length
        if (length < 0) {
            return
        }
        for (let i = 0; i < length; i  ) {
            var fn = fns[i]
            if (typeof fn != "function") {
                throw new Error(`index postion ${i} must be function`)
            }
        }
        //轮流执行函数 返回结果对象
        return function (...args) {
            var result = fns[0].apply(this, args)
            for (let i = 1; i < length; i  ) {
                var fn = fns[i]
                result = fn.apply(this, [result])
            }
            return result
        }
    }
    var newfn = composeFn(sum, pow)
    console.log(newfn(5)); //100

with语句、eval函数(拓展知识)

with语句 扩展一个语句的作用域链,不推荐使用有兼容性问题

eval 允许执行一个代码字符串。他是一个特殊函数可以将传入的字符串当作js代码执行

  • 可读性差
  • 有注入风险
  • 必须经过解释器 不会得到引擎的优化

严格模式的使用

js的局限性 :

  • JavaScript 不断向前发展且并未带来任何兼容性问题;
  • 新旧代码该新模式对于向下兼容有帮助但是也有问题出现
  • 就是创造者对于js的不完善之处会一直保留

ES5标准中提出了严格模式的概念,以更加严格的方式对代码进行检测和执行

只需要在代码的开头或者函数的开头 加入use strict就可以开启严格模式

JS对象增强

数据属性描述符

我们的属性一般定义在对象的内部或者直接添加到对象内部,但是这种方式我们就不能对属性进行一些限制,比如这个属性是否是可以通过delete删除,是否可以for-in遍历的时候被遍历出来等等

PS: 一个属性进行比较精准的操作控制,就可以使用属性描述符。

  • 通过属性描述符可以精准的添加或修改对象的属性
  • Object.defineProperty 来对属性进行添加或者修改

这个方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

代码语言:javascript复制
 Object.defineProperty() 
属性描述符分类

分为两类:

  • 数据属性
  • 存取属性
数据属性描述符

Configurable:表示属性是否可以通过delete删除属性,是否可以修改它的特性

  • 使用对象定义属性的时候为true
  • 使用属性描述符来定义的时候 默认为false

Enumerable:表示属性是否可以通过for-in或者Object.keys()返回该属性;

  • 直接对象内定义的时候 为true
  • 通过属性描述符定义为false

Writable:表示是否可以修改属性的值;

  • 直接对象内定义的时候 为true
  • 通过属性描述符定义为false

value:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改

  • 默认情况下这个值是undefined

使用案例

代码语言:javascript复制
    var obj = {
        name: "whit",
        age: 12
    }
    Object.defineProperty(obj, "name", {
        configurable: false,
        enumerable: false,
        writable: false,
        value: "1234"
    })
存取属性描述符

Configurable&Enumerable 也是存取属性描述符

get:获取属性时会执行的函数。默认为undefined

set:设置属性时会执行的函数。默认为undefined

同时定义多个属性

代码语言:javascript复制
    // 多个属性调用
    Object.defineProperties(obj, {
        name: {
            configurable: false,
            enumerable: false
        },
        age: {
            enumerable: false,
            writable: false,
        }
    })

对象方法补充

获取对象的属性描述符:

  • getOwnPropertyDescriptor
  • getOwnPropertyDescriptors

禁止对象扩展新属性:preventExtensions

  • 给一个对象添加新的属性会失败(在严格模式下会报错);

密封对象,不允许配置和删除属性:seal

  • 实际是调用preventExtensions
  • 并且将现有属性的configurable:false

冻结对象,不允许修改现有属性: freeze

  • 实际上是调用seal
  • 并且将现有属性的writable: false

代码案例

代码语言:javascript复制
    // 阻止对象的拓展
    Object.preventExtensions(obj)
    obj.address = 12
    //密封对象 不能进行配置
    Object.seal(obj)
    delete obj.name
    // 冻结对象
    Object.freeze(obj)
    obj.name = "ske"

0 人点赞