一、高级函数
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 函数绑定
这个技巧常常和回调函数与事件处理一起使用,以便在将函数作为变量传递的同时保留代码执行环境