一、高级函数

1-1 安全的类型检测

想到类型检测,那么脑海里第一反应应该就是在 Javascript 的世界中到底有哪些类型(这真的是一个非常古老的问题了)

我们大致分为 2 类: 基本类型 和 引用类型

其中 基本类型 包括了: string、number、bool、undefined、null

其中 引用类型 包括了: Array、Function、Object

那我们用 type 和 instanceof 分别来看下这几种数据类型判定返回的内容

为什么说 利用 type 和 instanceof 是不安全的类型检测

const str = 'test'
const num = 12
const bool = false
const unde = undefined
const nulls = null
const Array = [1,2,3,4]
const Object = {name: 'zzz'} const checkType = (type) => {
return typeof(type)
} // 使用 type 来判断
console.log(checkType(str)) // string
console.log(checkType(num)) // number
console.log(checkType(bool)) // boolean
console.log(checkType(unde)) // undefined
console.log(checkType(nulls)) // object
console.log(checkType(Array)) // object
console.log(checkType(Object)) // object
// 很显然 null、Array、Object 返回的都是 object 不够安全 ( bug 点 ) // 用 instanceof 来判断 const checkInstance = (type) => {
return type instanceof String
} console.log(checkInstance(str)) // 是 false 这是为什么呢? // 那么我们就需要来介绍下 instanceof 的原理了。

1-1-1 instanceof 的原理

instanceof 的的功能实现是 前者是否为后者的实例 , 具体的代码就是:

eg:


let res = a instanceof A // a 是 A 的实例
// A 是 a 的构造函数
// 同时 a 的 __proto__ == A 的 prototype 那么 a instanceof A == true 否则就等于 false

其中 有几个关键的点 如下:

  • 关于 constrcutor 、proto 、prototype、原型对象 这四个点的理解。

  • 推荐一篇好文章吧 prototype、proto、constructor 的三角关系

  • 回到上面 a.proto 指向的就是 a 的原型对象

  • A.prototype 指向的是 实例对象 的 原型对象

var Foo = function() {
this.setName = (name) => {
this.name = name
}
} var foo = new Foo Foo.prototype 指向 => 原型对象(理解为公共对象)
// 通过同一个构造函数实例化的多个对象具有相同的原型对象。经常使用原型对象来实现继承 Foo.prototype.constructor 指向 => 构造函数本身(Foo) foo.__proto__ 指向 => 原型对象(理解为公共对象) foo.constructor 指向 => 构造函数 (Foo)

1-2 作用域安全的构造函数

在全局作用域内调用函数构造函数,由于没有使用new,导致在全局作用域添加冗余的属性


function Person(name,job) {
this.name = name
this.job = job
} // 假如为使用 New 操作 var person = Person('zhangsan','sell') console.log(window.name, window.job) // zhangsan sell
console.log(person.name, person.job) // VM76:11 Uncaught TypeErrorr

这个问题是由this对象的晚绑定造成的

因此,需要在函数里面确认 this对象是正确类型的实例:

function Person(name){
if(this instanceof Person){
this.name = 'zhang';
} else {
return new Person(name)
}
} var person = Person('zhang')
console.log(window.name) // ''
console.log(person.name) // zhang

1-3 惰性载入函数

惰性载入表示函数执行的分支会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支去进行判断了。(节约算力)

1-3-1 应用场景

1、 AJAX 在不同浏览器下兼容性

2、 APP 内嵌 H5 不同环境下同一种功能方法,写法不一样

3、 H5 在不同平台下多处表现形式因为一个方法而展现的不一样。

1-3-2 注意的地方

1、应用越频繁,越能体现这种模式的优势所在

2、固定不变,一次判定,在固定的应用环境中不会改变

3、复杂的分支判断,没有差异性,不需要应用这种模式

1-3-3 Demo


const getCurEnv = () => {
// 当前环境为 chrome 浏览器环境
return window.navigator.userAgent.toLowerCase().match(/chrome/i) !== null
} const Person = function(name) {
this.name = name
} const http = {
created: function() {
if (getCurEnv()) {
console.log(this)
this.created = function() {
console.log('test1')
return new Person('zhang1')
}
console.log('test2')
return new Person('zhang2')
} else {
this.created = function() {
console.log('test3')
return new Person('zhang3')
}
}
},
Atest: ()=> {
console.log(this) // window {}
},
Ftest: function() {
console.log(this) // http {}
}
} http.created() // test2 Person {name: "zhang2"}
http.created() // test1 Person {name: "zhang1"} // 实际有效的 惰性载入函数 上面的 二个 方法返回的值 其实是一样的。这样惰性载入函数 才是真实有效。

1-4 函数绑定

这个技巧常常和回调函数与事件处理一起使用,以便在将函数作为变量传递的同时保留代码执行环境

很多JavaScript库实现了一个可以将函数绑定到指定环境的函数,这个函数一般都叫做bind()。一个简单的bind()函数接受一个函数和一个环境,并返回一个给的环境中调用给定函数的函数,并且将所有参数原封不动传递过去。这个函数返回的是一个闭包。

上面的语言描述总是很虚无飘渺,我们来直接上Demo:

1-4-1 Demo


var obj1 = {
name: 'zhang',
getName: function() {
console.log(arguments[0][2], 'obj1')
return this.name
}
} var obj2 = {
name: 'lisi',
getName: function() {
console.log(arguments, 'obj2')
return this.name
}
} function Bind(fn, context) {
return fn.call(context, arguments)
} Bind(obj1.getName,obj2,'xxxxx') // Arguments [Arguments(3), callee: ƒ, Symbol(Symbol.iterator): ƒ] "obj1"
// 'lisi'
// 这里我们对于 arguments 的 理解和操作来说都是比较陌生,那么下面 我们再来介绍下
// arguments 具体是什么。

1-4-2 arguments

类数组 (Array-like)

  • 可以用下标访问每个元素
  • 有 length 属性
  • arguments 的数据类型为 object
  • 可以使用 for 和 for-in 方法
  • 不具备 Array 原生方法

Demo


var test = function() {
console.log(arguments)
console.log(arguments[0])
console.log(arguments.length)
console.log(typeof arguments)
for(var i = 0; i<arguments.length; i++) {
var ele = arguments[i]
console.log(ele)
}
for(x in arguments) {
console.log(arguments[x])
}
// arguments.split(' ')
// Uncaught TypeError: arguments.split is not a function
} test(1,2,3,4)
// Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 1
// 4
// object
// 1 2 3 4
// 1 2 3 4

将类数组 转化为 数组

  • 方法一 :
var test = function() {
console.log(arguments)
var arrArg = Array.prototype.slice.call(arguments)
console.log(arrArg)
} test(1,2,3,4) // [1, 2, 3, 4]
  • 方法二 :
var test = function() {
console.log(arguments)
var arrArg = Array.from(arguments)
console.log(arrArg)
} test(1,2,3,4) // [1, 2, 3, 4]

1-4-3 ES5 中原生 bind() 方法 详解

文字解释起来还是比较吃力,那么我们还是 showCode~

Demo:

var obj = {
a: 1,
b: 2,
getCount: function(c, d) {
return this.a + this.b + c + d
}
}
console.log(obj.getCount(3,4)) // 10
window.a = window.b = 0
var funcs = obj.getCount
funcs(3,4) // 7

bind是function的一个函数扩展方法, bind 以后代码重新绑定了 func 内部的 this 指向(obj)

兼容 IE9 +

Demo:

var obj = {
a: 1,
b: 2,
getCount: function(c, d) {
return this.a + this.b + c + d
}
}
console.log(obj.getCount(3,4)) // 10
window.a = window.b = 100
var funcs = obj.getCount.bind(obj)
funcs(3,4) // 10
// var funcs = obj.getCount.bind(window)
// funcs(3,4) // 207

1-5 函数柯里化

又称部分求值。柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。

这增加了函数的适用性,但同时也降低了函数的适用范围。

文字的定义始终让人难以接受,还是 showCode 吧

Demo:假设你要写一个 记账的工具,然后记录每天的数据,最后统计整个星期的数据。

how ?

let weekCost = 0
const cost = function(num) {
weekCost += num
}
cost(100) // 100
cost(200) // 300
cost(300) // 600
这个时候每天都会进行一次 总账,这个是我不想看到的,因为不想每天都被这个总账看着心痛,毕竟工资不够花是常态。我就希望每个星期给我来一次总账刺激。
const currying = function(fn) {
let args = []
return function() {
if (arguments.length == 0) {
return fn.apply(this, args)
} else {
let test = [].push.apply(args,arguments)
// return fn.call(this, arguments)
}
}
} const costs = (function() {
let money = 0
return function() {
money = 0
for(let i = 0; i<arguments.length; i++) {
money += arguments[i]
}
return money
}
})() let cost = currying(costs) cost(100)
cost(100)
cost(100)
cost(100)
cost(100) console.log(cost()) // 500 cost(100)
cost(100) console.log(cost()) // 700

小结一:

上面的 dmeo 中,当调用 cost() 时,如果明确带上参数,表明此时并不进行真正的求值计算,而是把这些参数保存起来,此时让 cost() 函数返回另外一个函数。只有当我们以不带参数的形式执行 cost() 时,才利用前面保存的所有参数,真正开始求值计算。这是一个具象的函数颗粒化的方法。那么我们想把函数颗粒化抽象出来又需要怎么来概括呐? 												

《前端之路》之 JavaScript 高级技巧、高阶函数(一)的更多相关文章

  1. 廖老师JavaScript教程高阶函数-sort用法

    先来学习一个新词:高阶函数 高阶函数英文叫Higher-order function.那么什么是高阶函数? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那 ...

  2. 07.Javascript——入门高阶函数

    高阶函数英文叫Higher-order function..JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数 ...

  3. JavaScript系列--JavaScript数组高阶函数reduce()方法详解及奇淫技巧

    一.前言 reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值. reduce() 可以作为一个高阶函数,用于函数的 compose. reduce()方 ...

  4. python之路(4)高阶函数和python内置函数

    前言 函数式编程不用变量保存状态,不改变变量 内置函数 高阶函数 把函数当作参数传给另一个对象 返回值中包含函数 使用的场景演示: num_test = [1,2,10,5,8,7] 客户说 :对上述 ...

  5. Javascript中的高阶函数介绍

    高阶函数:高阶看上去就像是一种先进的编程技术的一个深奥术语,一开始我看到的时候我也这样认为的. Javascript的高阶函数 然而,高阶函数只是将函数作为参数或返回值的函数.以下面的Hello,Wo ...

  6. 如何在JavaScript中使用高阶函数

    将另一个函数作为参数的函数,或者定义一个函数作为返回值的函数,被称为高阶函数. JavaScript可以接受高阶函数.这种处理高阶函数的能力以及其他特点,使JavaScript成为非常适合函数式编程的 ...

  7. 《前端之路》之 JavaScript 进阶技巧之高阶函数(下)

    目录 第二章 - 03: 前端 进阶技巧之高阶函数 一.防篡改对象 1-1:Configurable 和 Writable 1-2:Enumerable 1-3:get .set 2-1:不可扩展对象 ...

  8. JavaScript ES6函数式编程(一):闭包与高阶函数

    函数式编程的历史 函数的第一原则是要小,第二原则则是要更小 -- ROBERT C. MARTIN 解释一下上面那句话,就是我们常说的一个函数只做一件事,比如:将字符串首字母和尾字母都改成大写,我们此 ...

  9. JavaScript进阶之高阶函数篇

    JavaScript进阶之高阶函数篇 简介:欢迎大家来到woo爷说前端:今天给你们带来的是JavaScript进阶的知识,接下来的系列都是围绕着JavaScript进阶进行阐述:首先我们第一篇讲的是高 ...

  10. JavaScript高阶函数

    所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...

随机推荐

  1. 微软黑科技强力注入,.NET C#全面支持人工智能

    微软黑科技强力注入,.NET C#全面支持人工智能,AI编程领域开始C#.Py--百花齐放 就像武侠小说中,一个普通人突然得到绝世高手的几十年内力注入,招式还没学,一身内力有点方 Introducin ...

  2. Coursera-AndrewNg(吴恩达)机器学习笔记——第二周编程作业

    一.准备工作 从网站上将编程作业要求下载解压后,在Octave中使用cd命令将搜索目录移动到编程作业所在目录,然后使用ls命令检查是否移动正确.如: 提交作业:提交时候需要使用自己的登录邮箱和提交令牌 ...

  3. Python_回调函数

    import os import stat def remove_readonly(func,path): #定义回调函数 os.chmod(path,stat.S_IWRITE) #删除文件的只读文 ...

  4. Struts标签库详解【2】

    ==================================================================== 需要在head中用<s:head />标签 一.控 ...

  5. spring+jotm+ibatis+mysql实现JTA分布式事务

    1 环境 1.1 软件环境  spring-framework-2.5.6.SEC01-with-dependencies.zip ibatis-2.3.4 ow2-jotm-dist-2.1.4-b ...

  6. java 23种设计模式教程

    设计模式分类 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模 ...

  7. SSM-MyBatis-17:Mybatis中一级缓存(主要是一级缓存存在性的证明,增删改对一级缓存会造成什么影响)

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 缓存------------------------------------------> 很熟悉的一个 ...

  8. PiggyMetrics windows 部署

    PiggyMetrics 是springcloud的demo,其特性就不细说了,主要描述在win10下部署的坑. 官网是:https://github.com/sqshq/PiggyMetrics 官 ...

  9. 在单用户模式下修改CentOS的root密码

    我们在使用CentOS的过程中可能会发生忘记root用户密码的情况,本文就从应用的角度简单介绍一下如何在单用户模式下修改root用户的密码. 开启CentOS,进入系统启动菜单 将光标停留在系统开机时 ...

  10. 你不知道的JavaScript--Item3 隐式强制转换

    JavaScript的数据类型分为六种,分别为null,undefined,boolean,string,number,object. object是引用类型,其它的五种是基本类型或者是原始类型.我们 ...