如何写好JavaScript | 青训营

41 阅读7分钟

Javascript

写在最前: Javascript包含了ECMAScript(核心,即ES)、DOM(文档对象模型)和BOM(浏览器对象模型)。 此文章只总结了笔者在开发过程中常用的一些基础Js知识点。

常见数据类型

Js包含6种原始数据类型和1种复杂数据类型。

  1. Undefined类型的数据只有一个特殊的值undefined,注意此值不是string,当声明了一个变量但没有赋值的时候相当于给该变量赋予了undefined值。
  2. Null类型,此类型在逻辑上相当于一个空对象的指针。
  3. Boolean,该类型有两个字面量值true和false。
  4. Number,将非数值转化为数值的函数:Number()、parseInt()、parseFloat(),Number()是转型函数,可以用于任何数据类型,后面两个函数主要是将字符串转化为数值。
  5. String,注意,字符串一旦创建就不可以改变。模板字面量(ES6新特性),使用反引号来定义字符串,其中保留换行符等转义字符,而且可以跨行定义字符串。(ps:模板字面量不是字符串,但在定义时立即求值并转换为字符串实例)

for-in和for-of
  1. for-in是一种严格的迭代语句,用于枚举对象中的非符号键属性。(无序的)
    let a = {
        b: 1,
        c: 2
    };
    for(const i in a) {
        console.log(i);
    }
    // 输出b和c
  1. for-of是一种严格的迭代语句,用于遍历可迭代对象的元素,注意事项同for-in。

作用域和闭包
  1. 作用域
    被var声明的变量(自动添加到最近的上下文,函数中就是函数局部上下文)会被提升到函数或全局作用域的顶部(变量提升),如果变量未声明就初始化则会被添加到全局上下文。
    let和var相似,但let的作用域是块级的(块级作用域由最近的一对大括号来决定,块级作用域不是上下文,在全局上下文用let声明的变量不会作为window对象的属性或方法),用let重复声明会报错。let适合再循环中声明迭代遍历,因为使用var会暴露
    const生命的变量除了只读,其他的和let一样,一般用于声明顶级原语和对象。
    let和const是es6新增的,尽量使用这两者来代替var
    在全局作用域中用let声明变量不会成为window对象的属性,用var会。
  2. 作用域链和闭包
    首先要了解执行上下文这一概念。变量或函数的上下文决定了它们可以访问哪些数据以及他们的行为。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上。全局上下文是最外层的上下文,根据ECMAScript实现的宿主环境,表示全局上下文的对象可能不一样,在浏览器中全局上下文是window。每个函数调用都有自己的上下文。当代码流进入函数时,函数的上下文会被推到一个上下文栈上,函数执行完毕后对应的上下文会出栈,控制权交给之前的执行上下文。上下文的代码在执行的时候,会创建变量对象的一个作用域链。 注意,上下文在其所有代码执行完毕之后都会被销毁。
    其次要了解什么是闭包。闭包是指引用了另一个函数作用域中变量的函数(通常是一个函数的返回值是另一个函数)。
    function fun(propertyName) {
        return function(obj) {
            console.log(obj[propertyName]);
        }
    }

在一个函数内部定义的函数会把包含他的函数的活动对象添加到自己的作用域链中,因此在此例子中匿名函数的作用域链中实际上包含着fun函数的活动对象。所以副作用是:fun的活动对象不能再它执行完毕后就销毁,因为匿名函数的作用域链中仍然有着对它的引用
注意,上下文对象是无法通过代码直接访问的,只是后台处理数据的时候会用到它。而且闭包会引起内存泄漏的问题 3. Global和window
window对象表示浏览器的实例。window对象在浏览器中有两重身份一个是ECMAScript中的Global对象 ,另一个是浏览器的Javascript接口(“可以理解成Global对象包含着window对象”)。window对象的属性在不同浏览器间可能差异很大。
在NodeJs环境下ECMAScript中的Global就是global对象。


函数
  1. 箭头函数 es6新增,箭头函数简介的语法很适合嵌入函数的场景。
    注意,①如果只有一个参数可以不用括号,如果没有参数或者有多个参数需要使用括号;②可以不用大括号,但这会改变函数的行为,使用大括号和常规的函数的函数体的功能一样,如果不使用大括号那么箭头函数后面只能有一行代码,而且大括号会隐式地返回这行代码的值;③箭头函数不能使用arguments、super、不能做构造函数,也没有prototype属性。
  2. this
    在标准函数中this引用的是把函数当成方法调用的上下文对象。在箭头函数中this引用的是定义箭头函数的上下文
  3. call、apply和bind(Function实例身上的三个方法)
    call和apply这两个方法都会以指定的this值来调用函数。apply的第一个参数是this值,第二个参数可以是Array实例或者arguments对象;call的作用和apply一样,但剩下的要传给被调用函数的参数是逐个传递的。但这两者都无法改变箭头函数this的指向。
    bind会创建一个新的函数实实例,其this的值会被绑定到传给bind()的对象。

String和Array身上的常用方法
  1. String
    let str = new String('hello')
    str.length //字符串长度
    str.charAt(2) //返回给定索引值位置的字符
    str.concat(' world') //返回'hello world',可接收多个参数
    str.slice(3) //从第四个,即索引值为3的位置往后截取
    str.substring(3) //同上
    str.substr(3) //同上
    str.slice(3, 7) //索引值3截取到索引值6(7-1)
    str.substring(3, 7) //同上
    str.substr(3, 7) //从索引值3开始截取,截7个
    str.indexOf('a') //从前往后找第一个a的索引,没找到返回-1
    str.lastIndexOf('a') //从后往前找第一个a的索引,没找到返回-1
    str.startsWith('abc') //是否以abc开头
    str.endsWith('abc') //是否以abc结尾
    str.includes('abc') //是否包含abc
    str.trim() //删除前后空格后返回结果
    str.repeat(2) //复制两次后返回
    str.toUperCase() //转化为大写
    str.toLowerCase() //转化为小写
    str.match() //正则匹配
  1. Array
    Array.from('hello') //自负床会被拆分成单字符数组
    let arr = [1, 2, 3]
    let arr2 = Array.from(arr, x => x + 1) //[2, 3, 4]
    Array.of(1, 2, 3) //[1, 2, 3]
    arr.length //素组长度
    arr.push() //栈推入
    arr.pop() //栈弹出
    arr.shift() //队弹出

    function compare(v1, v2) {
      if(v1 < v2) {
        return -1
      }else if (v1 > v2) {
        return 1
      }else {
        return 0
      }
    }
    arr.sort(compare) //升序 -1和1对换是降序
    arr.sort((a, b) => { a < b ? 1 : a > b ? -1 : 0}) //同上
    arr.concat() //在现有数组的全部元素基础上添加新多个元素并返回新数组
    arr.slice(a) //从a开始往后截取
    arr.slice(a, b) //从a截取到b前一位
    arr.splice(a, b) //从a开始删除b个
    arr.splice(a, 0, b) //在从位置a开始插入b,第三个元素可以有多个
    arr.splice(a, 1, c) //从第a个位置删除1个,再插入b,可以做替换操作
    arr.indexOf(a) //从前往后找a的索引
    arr.lastIndexOf(a) //从后往前找a的索引
    arr.includes(a) //是否包含a
    //迭代方法有两个参数:以每一项为参数运行的函数和上下文对象,后者常被省略
    arr.every(fun, target) //对每一项都进行传入的函数,如果每一项都返回true则返回true
    arr.filter(fun, target) //~,函数返回true的项组成数组后返回
    arr.forEach(fun, target) //~,对数组每一项运行传入的参数但没有返回值
    arr.map(fun, target) //~,返回执行每次操作后的数组
    arr.some(fun, target) //~,如果有一项返回true则返回true
    //迭代方法都不影响原数组