目录

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

  1. 我们大致分为 2 类: 基本类型 引用类型
  2. 其中 基本类型 包括了: stringnumberboolundefinednull
  3. 其中 引用类型 包括了: ArrayFunctionObject

那我们用 type 和 instanceof 分别来看下这几种数据类型判定返回的内容
为什么说 利用 type 和 instanceof 是不安全的类型检测

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

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

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

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

  • 关于 constrcutor 、proto 、prototype、原型对象 这四个点的理解。
  • 推荐一篇好文章吧 prototype、proto、constructor 的三角关系

  • 回到上面 a.__proto__ 指向的就是 a 的原型对象
  • A.prototype 指向的是 实例对象 的 原型对象

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

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

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

这个问题是由this对象的晚绑定造成的
因此,需要在函数里面确认 this对象是正确类型的实例:

  1. function Person(name){
  2. if(this instanceof Person){
  3. this.name = 'zhang';
  4. } else {
  5. return new Person(name)
  6. }
  7. }
  8. var person = Person('zhang')
  9. console.log(window.name) // ''
  10. console.log(person.name) // zhang

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

1、 AJAX 在不同浏览器下兼容性
2、 APP 内嵌 H5 不同环境下同一种功能方法,写法不一样
3、 H5 在不同平台下多处表现形式因为一个方法而展现的不一样。

1、应用越频繁,越能体现这种模式的优势所在
2、固定不变,一次判定,在固定的应用环境中不会改变
3、复杂的分支判断,没有差异性,不需要应用这种模式

  1. const getCurEnv = () => {
  2. // 当前环境为 chrome 浏览器环境
  3. return window.navigator.userAgent.toLowerCase().match(/chrome/i) !== null
  4. }
  5. const Person = function(name) {
  6. this.name = name
  7. }
  8. const http = {
  9. created: function() {
  10. if (getCurEnv()) {
  11. console.log(this)
  12. this.created = function() {
  13. console.log('test1')
  14. return new Person('zhang1')
  15. }
  16. console.log('test2')
  17. return new Person('zhang2')
  18. } else {
  19. this.created = function() {
  20. console.log('test3')
  21. return new Person('zhang3')
  22. }
  23. }
  24. },
  25. Atest: ()=> {
  26. console.log(this) // window {}
  27. },
  28. Ftest: function() {
  29. console.log(this) // http {}
  30. }
  31. }
  32. http.created() // test2 Person {name: "zhang2"}
  33. http.created() // test1 Person {name: "zhang1"}
  34. // 实际有效的 惰性载入函数 上面的 二个 方法返回的值 其实是一样的。这样惰性载入函数 才是真实有效。

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

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

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

  1. var obj1 = {
  2. name: 'zhang',
  3. getName: function() {
  4. console.log(arguments[0][2], 'obj1')
  5. return this.name
  6. }
  7. }
  8. var obj2 = {
  9. name: 'lisi',
  10. getName: function() {
  11. console.log(arguments, 'obj2')
  12. return this.name
  13. }
  14. }
  15. function Bind(fn, context) {
  16. return fn.call(context, arguments)
  17. }
  18. Bind(obj1.getName,obj2,'xxxxx')
  19. // Arguments [Arguments(3), callee: ƒ, Symbol(Symbol.iterator): ƒ] "obj1"
  20. // 'lisi'
  21. // 这里我们对于 arguments 的 理解和操作来说都是比较陌生,那么下面 我们再来介绍下
  22. // arguments 具体是什么。

类数组 (Array-like)

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

Demo

  1. var test = function() {
  2. console.log(arguments)
  3. console.log(arguments[0])
  4. console.log(arguments.length)
  5. console.log(typeof arguments)
  6. for(var i = 0; i<arguments.length; i++) {
  7. var ele = arguments[i]
  8. console.log(ele)
  9. }
  10. for(x in arguments) {
  11. console.log(arguments[x])
  12. }
  13. // arguments.split(' ')
  14. // Uncaught TypeError: arguments.split is not a function
  15. }
  16. test(1,2,3,4)
  17. // Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
  18. // 1
  19. // 4
  20. // object
  21. // 1 2 3 4
  22. // 1 2 3 4

将类数组 转化为 数组

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

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

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

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

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

又称部分求值。柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。
这增加了函数的适用性,但同时也降低了函数的适用范围。
文字的定义始终让人难以接受,还是 showCode 吧
Demo:假设你要写一个 记账的工具,然后记录每天的数据,最后统计整个星期的数据。
how ?

  1. let weekCost = 0
  2. const cost = function(num) {
  3. weekCost += num
  4. }
  5. cost(100) // 100
  6. cost(200) // 300
  7. cost(300) // 600
  1. 这个时候每天都会进行一次 总账,这个是我不想看到的,因为不想每天都被这个总账看着心痛,毕竟工资不够花是常态。我就希望每个星期给我来一次总账刺激。
  1. const currying = function(fn) {
  2. let args = []
  3. return function() {
  4. if (arguments.length == 0) {
  5. return fn.apply(this, args)
  6. } else {
  7. let test = [].push.apply(args,arguments)
  8. // return fn.call(this, arguments)
  9. }
  10. }
  11. }
  12. const costs = (function() {
  13. let money = 0
  14. return function() {
  15. money = 0
  16. for(let i = 0; i<arguments.length; i++) {
  17. money += arguments[i]
  18. }
  19. return money
  20. }
  21. })()
  22. let cost = currying(costs)
  23. cost(100)
  24. cost(100)
  25. cost(100)
  26. cost(100)
  27. cost(100)
  28. console.log(cost()) // 500
  29. cost(100)
  30. cost(100)
  31. console.log(cost()) // 700

小结一:

  1. 上面的 dmeo 中,当调用 cost() 时,如果明确带上参数,表明此时并不进行真正的求值计算,而是把这些参数保存起来,此时让 cost() 函数返回另外一个函数。只有当我们以不带参数的形式执行 cost() 时,才利用前面保存的所有参数,真正开始求值计算。这是一个具象的函数颗粒化的方法。那么我们想把函数颗粒化抽象出来又需要怎么来概括呐?
版权声明:本文为erbingbing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/erbingbing/p/9544420.html