JavaScript 进阶征途:解锁Function奥秘,深掘Object方法精髓

本文主要是介绍JavaScript 进阶征途:解锁Function奥秘,深掘Object方法精髓,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


在这里插入图片描述



个人主页:学习前端的小z

个人专栏:JavaScript 精粹

本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论!
在这里插入图片描述

文章目录

  • 🈵Function方法 与 函数式编程
    • 💝1 call
    • 💝2 apply
    • 💝3 柯理化函数(currying)
    • 💝4 bind
    • 💝5 偏函数 (partial)
    • 💝6 通道函数(compose)
      • 🏆6.1 柯理化与偏函数区别
  • 🈵Object 方法 深入
    • 💝1 Object.defineProperty()
    • 💝2 Object.entries()
    • 💝3 Object.keys()
    • 💝4 Object.fromEntries()
    • 💝5 Object.preventExtensions()


在这里插入图片描述


🈵Function方法 与 函数式编程

💝1 call

语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

    let myName = 'goudan';let myAge = 13;     function showMsg(msg){return (msg + '').toLowerCase();}showName(myName); // 'goudan'

这段代码很容易就能看懂,在实际开发工作中,我们会处理不同的数据集合,这时候声明单一变量已经无法满足胃口,需要通过json的形式来存储数据

    let person = {name:"kyogre",age:13,hobby:"coding"}let newPerson  ={name:'dachui',age:99,hobby:'eat'}function showMsg(msg){return  (msg + '').toLowerCase();}showMsg(person.name) // 'kyogre'showMsg(newPerson.name) // 'dachui'

存储数据的方式发生了改变,但是我们处理数据的方式还是那么。。。古老,如果业务复杂一点

    function format(msg){return msg+''.toLowerCase();}function show(msg){return '信息的内容是:'+ format(msg);}show(person.name) // '信息内容是:kyogre'show(newPerson.name) // '信息内容是:dachui'

显示的传递上下文对象 (穿参)使得业务越来越复杂,这种叠加会让开发变得冗余和难以解读,bug和闭包横飞

那我们看看通过this如何清晰解决问题

通常this不会指向函数自身,而是调用函数的对象主体。当然,我们可以强制让function自身成为对象主体,这个以后咱们讨论; json本身就是对象,我们是否可以这样:

    const person = {name:"kyogre",age:13,hobby:"coding"}const newPerson  ={name:'dachui',age:99,hobby:'eat'}function format(){return this.name+''.toLowerCase();}

问题来了,不让穿参这个format中的this指向谁呢? 指向调用format的对象本身,可是调用主体并不明确,可能是person也可能是newPerson,这时回过头看看call的定义吧:调用一个对象的一个方法,以另一个对象替换当前对象。 将函数内部执行上下文对象由原始对象替换为指定对象

        const name = '我叫window';    //全局变量 非严格模式下都为 window的属性   window.name;function format(){return  (this.name + '').toLowerCase();}format(); //'我叫window';     

不要惊慌,本身他就是这样window会作为调用顶级作用域链函数的对象主体;这里的this默认为 window, 用person对象代替window这个默认this主体去执行format会怎么样呢

        format.call(person); // kyogre format.call(newPerson);// dachuifunction show(){return '信息的内容是:'+ format.call(this);}show.call(person); // 信息的内容是:kyogre

感觉自己了解了this和call的小明,已经肆无忌惮的笑了起来,这样他就可以从繁重的回调与参数传递中解脱了,并且能够实现方法的初级模块化。


下面可以用call做一些平常的操作

    function isArray(object){return Object.prototype.toString.call(object) == '[object Array]';}// 借用Object原型上的toString方法来验证下对象是否是数组?function accumulation(){return [].reduce.call(arguments,(a,b)=>{return a+b}}//让不能使用数组方法的arguments类数组集合使用借用数组的reduce方法return Array.prototype.forEach.call($$('*'),(item)=>{item.style.border = '1px solid red';}

💝2 apply

语法:func.apply(thisArg, [argsArray])

call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

const person = {fullName: function(city, country) {return this.firstName + " " + this.lastName + "," + city + "," + country;}
}
const person1 = {firstName:"John",lastName: "Doe"
}
person.fullName.apply(person1, ["Oslo", "Norway"]);
Math.max.apply(null, [1,2,3]); // 3const arr = [1,2,3];
const otherArr = [3,4,5];
arr.push.apply(arr,otherArr);
console.log(arr); // [1, 2, 3, 3, 4, 5]

💝3 柯理化函数(currying)

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

  function currying(fn) {const args = Array.prototype.slice.call(arguments, 1);const inlay = function () {if (arguments.length === 0) {return fn.apply(this, args);}if(arguments.length > 0 ){Array.prototype.push.apply(args, arguments);return inlay;}     }return inlay;}function add() {const vals = Array.prototype.slice.call(arguments);return vals.reduce((pre, val) => {return pre + val;});}let newAdd = currying(add, 1, 2, 3);newAdd(4, 5);newAdd(6, 7)(6)(2, 3);console.log(newAdd()); //39

💝4 bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法:

function.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用[new](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new)运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 this将被视为新函数的thisArg
  • arg1, arg2, …
    • 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

返回值:

返回一个原函数的拷贝,并拥有指定的 **this** 值和初始参数。

const OBJ = {petName: 'kyogre',qq: '2971411050',sayHi: function () {console.log(`我是${this.petName} 很高兴认识你`)}
}let sayHi = OBJ.sayHi;
sayHi(); //我是undifined 很高兴认识你  ps: this非严格模式指向了window window.petName不存在let sayHi = OBJ.sayHi.bind(OBJ);
sayHi(); //我是kyogre 很高兴认识你 ps:通过bind强制绑定sayHi函数内部的this指向OBJ

💝5 偏函数 (partial)

在计算机科学中,局部应用(偏函数)是指固定一个函数的一些参数,然后产生另一个更小元的函数。(什么是元?元是指函数参数的个数,比如一个带有两个参数的函数被称为二元函数。)

bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

function list() {return Array.prototype.slice.call(arguments);
}function addArguments(arg1, arg2) {return arg1 + arg2
}const list1 = list(1, 2, 3); // [1, 2, 3]const result1 = addArguments(1, 2); // 3// 创建一个函数,它拥有预设参数列表。
const leadingThirtysevenList = list.bind(null, 37);// 创建一个函数,它拥有预设的第一个参数
const addThirtySeven = addArguments.bind(null, 37); const list2 = leadingThirtysevenList(); 
// [37]const list3 = leadingThirtysevenList(1, 2, 3); 
// [37, 1, 2, 3]const result2 = addThirtySeven(5); 
// 37 + 5 = 42 const result3 = addThirtySeven(5, 10);
// 37 + 5 = 42 ,第二个参数被忽略

💝6 通道函数(compose)

按照顺序

  function toUpperCase(str){return str.toUpperCase(); // 将字符串变成大写}function add(str){return str + '!!!'; // 将字符串拼接}function split(str){return str.split(''); // 将字符串拆分为数组}function reverse(arr){return arr.reverse(); // 将数组逆序}function join(arr){return arr.join('-'); // 将数组按'-'拼接成字符串}function compose(){const args = Array.prototype.slice.call(arguments); // 转换为数组使用下面的方法return function(x){return args.reduceRight(function(result, cb){return cb(result);}, x);}}const f = compose(add, join, reverse, split, toUpperCase);console.log( f('cba') ); // A-B-C!!!

🏆6.1 柯理化与偏函数区别

  • 柯里化是将一个多参数函数转换成多个单参数函数,也就是将一个 n 元函数转换成 n 个一元函数。

  • 局部应用则是固定一个函数的一个或者多个参数,也就是将一个 n 元函数转换成一个 n - x 元函数。


在这里插入图片描述


🈵Object 方法 深入

Object.getOwnPropertyNames()
//方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。Object.getPrototypeOf() 
//方法返回指定对象的原型(内部[[Prototype]]属性的值)。Object.getOwnPropertyDescriptors() 
//方法用来获取一个对象的所有自身属性的描述符。Object.getOwnPropertyDescriptor()
//方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)Object.assign() 
//方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。Object.create()
//方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 (请打开浏览器控制台以查看运行结果。)Object.freeze() 
//方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。Object.isFrozen()
//方法判断一个对象是否被冻结。Object.isSealed() 
//方法判断一个对象是否被密封。hasOwnProperty() 
//方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。isPrototypeOf() 
//方法用于测试一个对象是否存在于另一个对象的原型链上。Object.is() 
//方法判断两个值是否为同一个值。

💝1 Object.defineProperty()

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

语法

Object.defineProperty(obj, prop, descriptor)

参数

  • obj

    要定义属性的对象。

  • prop

    要定义或修改的属性的名称或 Symbol

  • descriptor

    要定义或修改的属性描述符。

返回值

被传递给函数的对象。

该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的,在枚举对象属性时会被枚举到(for...in 或 Object.keys 方法),可以改变这些属性的值,也可以删除这些属性。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的。对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。这两种描述符都是对象。它们共享以下可选键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):
  • configurable

    当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false

  • enumerable

    当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false

数据描述符还具有以下可选键值:

  • value

    该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined

  • writable

    当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。 默认为 false

存取描述符还具有以下可选键值:

  • get

    属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined

  • set

    属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined

描述符默认值汇总

  • 拥有布尔值的键 configurableenumerablewritable 的默认值都是 false
  • 属性值和函数的键 valuegetset 字段的默认值为 undefined

描述符可拥有的键值

configurableenumerablevaluewritablegetset
数据描述符可以可以可以可以不可以不可以
存取描述符可以可以不可以不可以可以可以

如果一个描述符不具有 valuewritablegetset 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 valuewritablegetset 键,则会产生一个异常。

记住,这些选项不一定是自身属性,也要考虑继承来的属性。为了确认保留这些默认值,在设置之前,可能要冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null)__proto__ 属性指向 null

Object.defineProperties() 可以对多条属性进行修改

var obj = {};
Object.defineProperties(obj, {'property1': {value: true,writable: true},'property2': {value: 'Hello',writable: false}// etc. etc.
});

💝2 Object.entries()

方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

语法

Object.entries(obj)

参数

  • obj

    可以返回其可枚举属性的键值对的对象。

返回值

​ 给定对象自身可枚举属性的键值对数组。

const object1 = {a: 'somestring',b: 42
};for (const [key, value] of Object.entries(object1)) {console.log(`${key}: ${value}`);
}// expected output:
// "a: somestring"
// "b: 42"
// order is not guaranteed

💝3 Object.keys()

方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。。

语法

Object.keys(obj)

参数

  • obj

    要返回其枚举自身属性的对象。

返回值

​ 一个表示给定对象的所有可枚举属性的字符串数组。

const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

💝4 Object.fromEntries()

方法把键值对列表转换为一个对象。

语法

Object.fromEntries(iterable);

参数

  • iterable

    类似 ArrayMap 或者其它实现了可迭代协议的可迭代对象。

返回值

一个由该迭代对象条目提供对应属性的新对象

const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }

💝5 Object.preventExtensions()

方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。

语法

Object.preventExtensions(obj)

参数

  • obj

    将要变得不可扩展的对象。

返回值

已经不可扩展的对象。

var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2;  // true// 字面量方式定义的对象默认是可扩展的.
var empty = {};
Object.isExtensible(empty) //=== true// ...但可以改变.
Object.preventExtensions(empty);
Object.isExtensible(empty) //=== false// 使用Object.defineProperty方法为一个不可扩展的对象添加新属性会抛出异常.
var nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // 抛出TypeError异常// 在严格模式中,为一个不可扩展对象的新属性赋值会抛出TypeError异常.
function fail()
{"use strict";nonExtensible.newProperty = "FAIL"; // throws a TypeError
}
fail();const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }

在这里插入图片描述


参考 : http://bclary.com/log/2004/11/07/#a-11.9.3

在这里插入图片描述


这篇关于JavaScript 进阶征途:解锁Function奥秘,深掘Object方法精髓的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/976715

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象