JS技巧技法总结——闭包原理、数组展平、前端语音、Proxy 数据绑定和监听、计数器

2019/08/08 JavaScript 本文共6638字,阅读全文约需22分钟 本文总阅读量

开发、阅读、学习中接触到的一些知识点。

JS技巧

JS计数器的几种实现

  • 全局变量 javascript let count = 0; const countUp = () => count++;
  • 闭包
       // javascript
        const countUp = (() => {
        let count = 0;
        return () => {
       return ++count;
        };
        })();
        console.log(countUp()); // 1
        console.log(countUp()); // 2
    
  • 函数属性
       // javascript
        let countUp = () => {
        return ++countUp.count;
        };
        countUp.count = 0;
        console.log(countUp()); // 1
        console.log(countUp()); // 2
    
  • 函数属性(TS)
       interface Counter {
        (): void; // 这里定义Counter这个结构必须包含一个函数,函数的要求是无参数,返回值为void,即无返回值
        count: number; // 而且这个结构还必须包含一个名为count、值的类型为number类型的属性
        }
        const getCounter = (): Counter => { // 这里定义一个函数用来返回这个计数器
        const c = () => { // 定义一个函数,逻辑和前面例子的一样
       c.count++;
        };
        c.count = 0; // 再给这个函数添加一个count属性初始值为0
        return c; // 最后返回这个函数对象
        };
        const counter: Counter = getCounter(); // 通过getCounter函数得到这个计数器
        counter();
        console.log(counter.count); // 1
        counter();
        console.log(counter.count); // 2
    

前端语音

  • 语音播报:在项目中需要对ajax请求返回的消息进行语音播报,str 为返回的data
      //语音播报
      function voiceAnnouncements(str){
          //百度
          var url = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text=" + encodeURI(str);        // baidu
          var n = new Audio(url);
          n.src = url;
          n.play();
      }
      voiceAnnouncements('你好,今天吃的什么?')
    
  • React Native Text-To-Speech library for Android and iOS

  • 语音识别:
    • 用语音控制自己的网站 annyang: A tiny JavaScript Speech Recognition library that lets your users control your site with voice commands.
      annyang has no dependencies, weighs just 2 KB, and is free to use and modify under the MIT license.

数组展开的N种方法

  • 循环加递归
  • flat
  • flatMap
  • toString后split
  • join后split

在react函数式组件中使用防抖与节流函数

const handleChange = value => {
  console.log(value)
}

const delayedQuery = useCallback(_.debounce(handleChange, 2000), []);
const changeDebounce = (value) => delayedQuery(value);
// 调用:onChange={changeDebounce}

参考:在react函数式组件中使用防抖与节流函数
一起围观由React Hooks防抖引发的面试翻车现场

排序及实用算法

实现中文按照拼音排序

  • 可以使用localeCompare() 方法来实现中文按照拼音排序
    var array = ['白鸽', '麻雀', '大象', '', '', ""];
    array = array.sort(function compareFunction(item1, item2) {
        return item1.localeCompare(item2);
    });
    //["白鸽", "大象", "狗", "鸡", "麻雀", "猫"]
    

    一个对象数组按照另一个数组排序

    sortFunc = (propName, referArr) => {
      return (prev, next) => {
        return referArr.indexOf(prev[propName]) - referArr.indexOf(next[propName])
      }
    }
     // 按照年龄age的顺序给person排序 
     const age = [33, 11, 55, 22, 66]; 
     const person = [
        {age: 55, weight: 50},
        {age: 22, weight: 42},
        {age: 11, weight: 15},
        {age: 66, weight: 56},
        {age: 33, weight: 68}]
     person.sort(sortFunc('age', age)) 
     <!-- 结果 -->
     [
        {"age": 33,"weight": 68},
        {"age": 11,"weight": 15},
        {"age": 55,"weight": 50},
        {"age": 22,"weight": 42},
        {"age": 66,"weight": 56}
     ]
    

使用 Proxy 来实现一个数据绑定和监听

Proxy简介:

let p = new Proxy(target, handler);
// `target` 代表需要添加代理的对象
// `handler` 用来自定义对象中的操作
let onWatch = (obj, setBind, getLogger) => {
  let handler = {
    get(target, property, receiver) {
      getLogger(target, property)
      return Reflect.get(target, property, receiver);
    },
    set(target, property, value, receiver) {
      setBind(value);
      return Reflect.set(target, property, value);
    }
  };
  return new Proxy(obj, handler);
};

let obj = { a: 1 }
let value
let p = onWatch(obj, (v) => {
  value = v
}, (target, property) => {
  console.log(`Get '${property}' = ${target[property]}`);
})
p.a = 2 // bind `value` to `2`
p.a // -> Get 'a' = 2

再谈闭包

一等公民的定义

  在编程语言中,一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。例如,字符串在几乎所有编程语言中都是一等公民,字符串可以做为函数参数,字符串可以作为函数返回值,字符串也可以赋值给变量。对于各种编程语言来说,函数就不一定是一等公民了,比如Java 8之前的版本。对于JavaScript来说,函数可以赋值给变量,也可以作为函数参数,还可以作为函数返回值,因此JavaScript中函数是一等公民。

动态作用域与静态作用域

  注意,是说作用域,不是类型。
  如果一门语言的作用域是静态作用域,那么符号之间的引用关系能够根据程序代码在编译时就确定清楚,在运行时不会变。某个函数是在哪声明的,就具有它所在位置的作用域。它能够访问哪些变量,那么就跟这些变量绑定了,在运行时就一直能访问这些变量。即静态作用域可以由程序代码决定,在编译时就能完全确定。大多数语言都是静态作用域的。(静态作用域可以由程序代码决定,在编译时就能完全确定,所以又叫做词法作用域(Lexcical Scope)。)
  动态作用域(Dynamic Scope)。也就是说,变量引用跟变量声明不是在编译时就绑定死了的。在运行时,它是在运行环境中动态地找一个相同名称的变量。在 macOS 或 Linux 中用的 bash 脚本语言,就是动态作用域的。

闭包

  闭包的内在矛盾是运行时的环境和定义时的作用域之间的矛盾。那么我们把内部环境中需要的变量,打包交给闭包函数,它就可以随时访问这些变量了。
  闭包这个概念,对于初学者来讲是一个挑战。其实,闭包就是把函数在静态作用域中所访问的变量的生存期拉长,形成一份可以由这个函数单独访问的数据。正因为这些数据只能被闭包函数访问,所以也就具备了对信息进行封装、隐藏内部细节的特性。

闭包与面向对象

  听上去是不是有点儿耳熟?封装,把数据和对数据的操作封在一起,这不就是面向对象编程嘛!一个闭包可以看做是一个对象。反过来看,一个对象是不是也可以看做一个闭包呢?对象的属性,也可以看做被方法所独占的环境变量,其生存期也必须保证能够被方法一直正常的访问。

闭包的实现

  • 函数要变成 JavaScript 的一等公民。也就是要能把函数像普通数值一样赋值给变量,可以作为参数传递给其他函数,可以作为函数的返回值。(怎么把函数作为一等公民呢?我们需要支持函数作为基础类型,这样就可以用这种类型声明变量。)
  • 要让内层函数一直访问它环境中的变量,不管外层函数退出与否。(把内部函数连同打包好的环境变量的值,创建一个 FunctionObject 对象,作为外层函数的返回值,给到调用者。)

归纳总结

  1. 因为JavaScript是静态作用域的,所以它内部环境中需要的变量在编译时就确定了,运行时不会改变;
  2. 因为JavaScript中,函数是一等公民,可以被调用,可以作为参数传递,可以赋值给变量,也可以作为函数返回值,所以它的运行时环境很容易变化;
  3. 当函数作为另一个函数(外层函数)的返回值返回时,其外层函数中的变量已经从调用栈弹出,但是我们必须让内层函数可以访问到它需要的变量,因此运行时的环境和定义时的作用域之间就产生矛盾;
  4. 所以我们把内部环境中需要的变量,打包交给内层函数(闭包函数),它就可以随时访问这些变量了,就形成了闭包。(内层函数作为返回值赋值给其他变量以后,外层函数就结束了,但内层函数仍能访问原来外层函数的变量,也能访问全局变量。)

    推荐阅读:闭包: 理解了原理,它就不反直觉了

JS函数柯里化

概念:

Currying是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术,即只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。示例:

// 普通的add函数
function add(x, y) {
    return x + y
}
// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}
add(1, 2)           // 3
curryingAdd(1)(2)   // 3

Currying化的好处

  • 参数复用:因为函数会返回另一个函数,所以可以参数复用;
  • 提前确认:比如浏览器兼容(addEventListener和attachEvent);
  • 延迟运行:比如bind的实现(bind会返回一个函数,其实bind的实现也用到了闭包);
  • 柯里化和闭包:利用柯里化机制的函数function就是闭包函数。通过柯里化,获取一个拥有记忆功能的函数,简化后续的多种计算操作,这就是闭包。

    curry性能

  • 存取arguments对象通常要比存取命名参数要慢一点
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的
  • 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
  • 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
    其实在大部分应用中,主要的性能瓶颈是在操作DOM节点上,这js的性能损耗基本是可以忽略不计的,所以curry是可以直接放心的使用。

    参考:详解JS函数柯里化

跨域

跨域资源共享 CORS 详解

跨域资源共享 CORS 详解——阮一峰

前端常见跨域解决方案(全)——segmentfault

mac上设置新版chrome浏览器跨域

打开一个新的可跨域的chrome窗口实现方法:

  1. 打开终端
  2. 输入下面的命令(需要替换路径中的yourname)
    open -n /Applications/Google\ Chrome.app/ --args --disable-web-security  --user-data-dir=/Users/yourname/MyChromeDevUserData/
    

cookie和session介绍

  在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。

session

  session和cookie的作用有点类似,都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。

cookie和session结合使用

  web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:

  1. 存储在服务端:通过cookie存储一个session_id,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个session_id,下次再次请求的时候,会把该session_id携带上来,服务器根据session_id在session库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。
  2. 将session数据加密,然后存储在cookie中。这种专业术语叫做client side session。flask采用的就是这种方式,但是也可以替换成其他形式。

JavaScript 设计模式

参考:W3Cschool链接

QAP相关介绍参考

  Qianniu Application Platform(QAP)是阿里千牛官方推出的、功能丰富的开发者套件。底层weex,上层有SDK接口rax库、以及rax的ui组件nuke。
  nuke是一套基于 Weex 容器 与 rax 开源渲染引擎的面向电商中后台场景的 UI 组件生态。

快速排序

// 实现一个数组排序
function sort(nums) {
  // 思路:分治
  // 1、随机取一个位置,以此为基准点,按照大小将数据分为左右两部分
  // 2、分别对左右进行相应的排序
  let len = nums.length // 数组长度缓存
  if(len <= 1 ) return nums
  let index = Math.floor(nums.length/2)// 取基准点
  let indexValue = nums[index] // 基准值
  let left = [] //左
  let right = [] //右
  for(let i = 0; i<len;i++) {
    if(nums[i] < indexValue){//排序
      left.push(nums[i])
    } else if (index !== i) {
      right.push(nums[i])
    }
  }
  return sort(left).concat(indexValue, sort(right))
}
console.log(sort([1,5,8,7,9]))
console.log(sort([1,5,8,17,29,10]))
console.log(sort([1,5,8,17,29,10, 10, 10, 10]))

Search

    欢迎与我交流

    江南逰子

    Table of Contents