js小知识合集

35 阅读9分钟

一、this

this是函数中都有的一个变量,因为this的值在书写的时候无法确定,只有在函数被调用的时候才能确定。

    function fn() {
        // 这里只是书写了this,但是无法确定this 的值,只有等到函数调用的时候才能确定
        console.log(this)
    }
  • 1.如果函数直接在全局作用域调用, 那么他的 this 就指向 window
    fn()   // window
  • 2.将 fn 函数放在一个对象中, 然后调用;此时内部的this 就指向了这个对象
    var obj = {
        name: '当你看到我的时候, 证明你看到了 obj 对象',
        cb: fn
    }

    console.log(fn())       // window
    console.log(fn)         // undefined
    console.log(this)       // ƒ
    console.log(obj.cb)     // ƒ

    obj.cb()                // {cb: ƒ}
    1. 将 函数 当成一个事件处理函数使用;此时 this 会指向事件源
    var box = document.querySelector('div')
    box.onclick = fn   // <div>div的一个标识</div>
    1. 将函数放在定时器中执行;此时this指向window
    setTimeout(fn, 0)      // window
    setInterval(fn, 10)    // window
  • 5.自执行函数;此时内部的this指向window
  • 语法: (书写要自执行的函数)(这里可以书写要传递给内部函数的实参)
    (fn)()  //  指向window

二、更改this

    function fn(num) {
        console.log(num, this)
    }

    var obj = {
        name: '我是 obj 对象'
    }

1.call

  • 在 JS中,每一个函数都自带一些方法,其中一个就是 call
  • 该方法是附加在函数调用后面使用, 可以忽略函数本身的 this 指向
  • 语法:函数名.call('将函数内部的this更改为谁?', '传递给函数的实参1', '传递给函数的实参2', '传递给函数的实参3' .......)
    fn.call(obj, 10010)         // 10010 { "name": "我是 obj 对象" }

2.apply

  • 该方法是附加在函数调用后面使用, 可以忽略函数本身的 this 指向\
  • 语法: 函数名.apply(要改变的 this 指向, [要给函数传递的参数1, 要给函数传递的参数2, ...])
    fn.apply(obj, [12345])        // 12345 { "name": "我是 obj 对象" }

3. bind

  • 和 call / apply 有一些不一样, 就是 bind 不会立即执行函数, 而是返回一个已经改变了 this 指向的函数
  • 语法: var newFn = 函数名.bind(要改变的 this 指向); newFn(传递参数)
    var res = fn.bind(obj, 'bind')
    res()            // bind { "name": "我是 obj 对象" }

三、箭头函数

  • 箭头函数只能简写函数表达式, 不能简写声明式函数
  • 语法: (函数的形参) => {函数体内要执行的代码}
    let obj = {
        fn1: function () { console.log(this) },
        // 箭头函数内部是没有 this 的, 你在这个位置使用的 this,就相当于找到了上一层级最近的一个 this 
        fn2: () => { console.log(this) }
    }
    obj.fn1()      // this === obj
    obj.fn2()      // this === window

1.写法

(1)简化写法:只有一个形参,小括号可省略

    let fn = (a) => { console.log(a) }
    let fn = a => { console.log(a) }
    fn(100)
  • 如果这一个形参有默认值, 那么必须添加小括号
    let fn = (a = 200) => { console.log(a) }
    fn(100)

(2)要执行的代码只有一行, 所以可以省略 {}

    let fn = a => a
    console.log(fn)             // a => a
    console.log(fn())           // undefined
    console.log(fn(1123))       // 1123

2.箭头函数与普通函数 this 的区别

  • (1)普通函数 this 指向与调用者
  • (2)箭头函数 this 取决于上下文

四、解构赋值

快速的从对象或者数组中去除成员的一个语法方式

1.解构对象

    // ES5 的方法想得到对象中的成员
    const obj = {
        name: 'Jack',
        age: 18,
        gender: '男'
    }
    let name = obj.name
    let age = obj.age
    let gender = obj.gender

    // 解构赋值的方式从对象中获取成员
    const obj = {
        name: 'Jack',
        age: 18,
        gender: '男'
    }
    let { name, age, gender } = obj
    /**
     *  前面的 {} 表示我要从 obj 这个对象中获取成员了
     *  name age gender 都带是 obj 中有的成员
     *  obj 必须是一个对象
    */

2.解构数组

    // ES5 的方式从数组中获取成员
    const arr = ['Jack', 'Rose', 'Tom']
    let a = arr[0]
    let b = arr[1]
    let c = arr[2]

    // 解构赋值的方式从数组中获取成员
    const arr = ['Jack', 'Rose', 'Tom']
    let [a, b, c] = arr
    /**
     *  前面的 [] 表示要从 arr 这个数组中获取成员了
     *  a b c 分别对应这个数组中的索引 0 1 2 的值
     *  arr 必须是一个数组
    */

五、模板字符串

表示字符串的时候使用`` (反引号)

反引号与单(双)引号的区别

(1)反引号可以换行书写

    let str = 'hello world' // 使用单引号或者双引号不能换行书写, 换行会报错

    // 使用反引号不会报错, 这样写也不会报错
    let str = `
        hello
        world
        `

(2)反引号可以直接在字符串里拼接变量, 不过需要借助 ${}

    // ES5 需要使用字符串拼接变量的时候
    let num = 100
    let str = 'hello' + num + 'world' + num
    console.log(str)

    let str2 = 'hellonumworldnum'
    console.log(str2)

    // 使用模板字符串
    let num = 100
    let str = `hello${num}world${num}`
    console.log(str)

六、展开运算符

ES6 里面新增了一个运算符 ...,叫做扩展运算符

作用

1.把数组展开

    let arr = [1, 2, 3, 4, 5]
    console.log(...arr) // 1 2 3 4 5

2.合并数组的时候使用

    let arr = [1, 2, 3, 4]
    let arr1 = [...arr, 5, 6, 7]
    console.log(arr1)

3.合并对象使用

    let obj = {
        name: 'Jack',
        age: 18
    }
    let obj1 = {
        ...obj,
        gender: '男'
    }
    console.log(obj1)

4.在函数传递参数时使用

    let arr = [1, 2, 3]
    function fn(a, b, c) {
        console.log(a)
        console.log(b)
        console.log(c)
    }
    fn(...arr)
    // 等价于 fn(1, 2, 3)

七、Map和Set 数据结构

共同点: 都不接受重复数据

1.Map 数据结构

  • 创建一个 Map 数据结构
  • 语法: var m = new Map([ [key, value], [key, value] ])
    // 创建了一个 空的 map 集合
    const oMap = new Map()

1. 向 map 中添加数据set()

    // 下边的三行代码也能执行, 但是不建议, 如果这样写和对象没有什么区别, 建议使用对象
    oMap.set('key', 'value')
    oMap.set('age', 18)
    oMap.set('name', '张三')
    
    // map 数据中, key 的值类型是没有限制的, 但是对象中, 只能是字符串
    const arr = [1, 2, 3]
    oMap.set(arr, [4, 5, 6])
    console.log(oMap)

2. 获取 map 中的一些数据

    console.log(oMap.get('age'))
    console.log(oMap.get('name'))
    console.log(oMap.get(arr))

3. has() 判断数据中是否拥有指定的 key

    console.log(oMap.has('name'))   // true
    console.log(oMap.has('qwe'))    // false

4. delete() 删除数据中的某一个

    oMap.delete('key')
    console.log(oMap)

5.clear() 清空数据集合

    oMap.clear()
    console.log(oMap)

6.forEach()

语法: 数据结构.forEach(function (value, key, origin) {})

    oMap.forEach(function (value, key, origin) {
        console.log(value, key)
    })

2.Set 数据结构

Set 内部不允许数据重复,可以利用Set去重

  • 创建 Set 数据结构
  • 语法: var s = new Set([数据1, 数据2, 数据2, 数据3, ... ])
    const arr = new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])

1.向集合中 添加数据

    oSet.add(10086)
    oSet.add('QF001')

    oSet.get()  // oSet.get is not a function,set 数据集合没有提供 get 方法

2. 判断数据集合中是否拥有这个数据

    console.log(oSet.has(5))
    console.log(oSet.has(500))

3. delete 删除某一个

    oSet.delete(1)
    oSet.delete(2)
    oSet.delete(3)
    console.log(oSet)
    

4. clear() 清空数据集合

    oSet.clear()
    console.log(oSet)

5.forEach() 方法

语法: 数据结构.forEach(function (value, key, origin) {})

    oSet.forEach(function (item, index, origin) {
        /**
          *  item 和 origin 和 数组中 forEach 的含义是相同的
          * 
          *  但是 index 不同, 因为 set 中 不允许使用 下标获取到内部的数据
          *  所以 index 的值不是下标, 而是数组的每一个值
          *  所以 在当前方法中, item 和 index 的值 永远相同
         */
        console.log(item, index, origin)
    })

利用 set 完成数组去重

    const arr = [...new Set([100, 100, 2, 2, 3, 3, 4, 4, 5, 5])]
    console.log(arr)    // [100, 2, 3, 4, 5]

八、对象简写语法

    1. 当你的对象 key 和 value 一模一样的时候,并且 value 是一个变量,那么可以省略一个不写
    var name = 'XXX'
    var obj = {
        name: name
    }

    var obj1 = {
        name
    }
  • 当对象内 key 对应的值是一个函数,并且不是箭头函数的时候,可以省略 function 关键字和冒号 不写
    var obj = {
        fn: function () {
            console.log(123)
        },
        fn1() {
            console.log(456)
        }
    }

九、模块化开发

  • 把完整功能,拆开成为一个一个的独立功能(模块),一个 JS 文件就是一个独立模块,根据业务需求,来进行模块整合。

  • 当模块化开发的时候,我们需要把多个逻辑书写在多个 JS 文件内,此时,每一个文件都是独立的文件,都是一个独立的模块作用域(文件作用域),该文件内,只能使用自己文件内的变量,不能使用其他文件内的变量

  • 注意:

    • 浏览器使用 ES6 模块化语法的要求,script 标签必须要有一个 type=module 的属性
    • 页面必须要在服务器上打开(可以借助live server)

1. 导出: 在一个文件内向外暴露一些内容

  • 语法1: export default 你要导出的数据
  • 一个文件只能导出一个 default 数据
    const num = 100
    const fn = () => {
        console.log('我是 header.js')
    }
    const obj = {
        num: num,
        fn: fn
    }
    export default obj
  • 语法2: export 定义变量 = 值
  • 一个文件可以导出多个新定义的变量
    export const s = 100
    export let a = 200

2. 导入: 导入该文件的同时, 拿到它向外暴露的内容

  • 语法:import 变量 from '文件'
    • 这个语法必须对应导出语法1
    import myObj = from './index.js'
  • 语法2:import { 导出的内容 } from '文件'
    • 这个语法必须对应导出语法2
    import {s, a} from './index.js'

十、拷贝对象

    const obj = {
        name: 'QF001',
        age: 18,
        info: {
            username: 'ZSSS',
            password: '123456'
        }
    }
    // console.log('原对象: ', obj)

    /**
      *  创建一个 和 obj 对象一模一样但是地址不同的一个对象
      *  标准: 修改 obj 对象的时候, 不会影响到新对象
    */


    /**
      *  因为 obj 对象是一个 引用数据类型, 所以在赋值的时候, 是将变量内部的地址, 给到了新变量种
      *  所以此时 newObj 和 obj 是同一个引用地址
      *  所以修改obj 会影响 newObj, 反过来也是相同
    */
    // const newObj = obj

    function fn(obj) {
        /**
          *  我们当前的 fn 函数, 会基于 一个形参(要求是一个对象)
          *  创建一个和它一模一样, 但是地址不同的 对象
          *  标准: 修改其中一个对象, 不会影响另外一个对象
        */
        const newObj = {}
        // 遍历 形参 obj, 拿到这对象的 所有属性和属性值, 然后添加到 newObj 中
        for (let key in obj) {

            // console.log(key, obj[key])
            const value = obj[key]

            if (typeof (value) === 'object') {
                /**
                  *  如果当前分支执行, 说明 value 的值是一个对象
                  *  如果是一个对象我们就不能够直接进行赋值, 我们需要封装一个函数
                  *  这个函数会基于 一个形参(要求是一个对象); 创建一个和他一模一样, 但是地址不同的对象
                */

                newObj[key] = fn(value)
            } else {
                // 如果当前分支执行, 说明 value 是基本数据类型, 那么直接赋值即可
                newObj[key] = value
            }

        }
        return newObj
    }

    const res = fn(obj) // fn 函数会基于全局变量 obj 帮我们创建一个和它一模一样, 但是不是一个地址的新对象
    // console.log('拷贝后的对象: ', res)

    res.name = 'res对象被我修改了'
    obj.age = 10086
    
    res.info.password = '我给 res 这个变量的密码修改了'
        
    console.log('源对象: ', obj)
    console.log('新对象: ', res)