JavaScript核心知识点(上篇)

看了不少 JS 的书籍和教程,每每会模糊焦点,这篇文章会作一个梳理,将 JS 中的重难点进行总结。javascript

文章分为上下两篇。上篇是基本知识,下篇是进阶知识。 html


#变量类型和计算

引例:java

  1. 值类型和引用类型的区别node

  2. typeof能判断哪些类型?面试

  3. 什么时候使用 === 什么时候使用 ==数组

  4. 手写深拷贝bash

变量类型

  • 值类型
  • 引用类型

值类型闭包

let a = 100
let b = a
a = 200
console.log(b) // 100
复制代码

引用类型app

let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
复制代码

分析:异步

值类型的变量,赋值时会从新将值拷贝一份,所以两个变量之间相互独立。

引用类型的变量名指向的是一个内存地址,真实的对象存储在该内存地址所指向的内存中(堆)。当变量进行赋值时let b = ab 也会指向 a 所指向的那块内存,两个变量实际上指向的是同一个对象。

常见值类型

const s = 'abc' // 字符串
const n = 100 // 数字
const b = true // 布尔
const s = Symbol('s') // Symbol
复制代码

常见值类型

const obj = { x: 100 } // 对象
const arr = ['a', 'b', 'c'] // 数组
const n = null // 空,特殊引用类型,指针指向为空地址
function fn() { } // 函数,特殊引用类型,但不用于存储数据,因此没有“拷贝,复制函数”这一说
复制代码

typeof 运算符

  • 识别全部值类型
  • 识别函数
  • 判断是不是引用类型(不可再细分)

识别全部值类型

let a                       typeof a // undefined
const s = 'abc'             typeof a // string
const n = 100               typeof n // number
const b = true              typeof b // boolean
const s = Symbol('s')       typeof s // symbol
复制代码

识别函数

typeof console.log // function
typeof function () { } // function
复制代码

判断是不是引用类型(不可再细分)

typeof null // object
typeof ['a', 'b'] // object
typeof { x: 100 } // object
复制代码

深拷贝

/** * 深拷贝 */
const obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c']
}

const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)
console.log(obj1.arr[0])

/** * 深拷贝 * @param {Object} obj 要拷贝的对象 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }

    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

    // 返回结果
    return result
}

复制代码

变量计算

类型转换:

  • 字符串拼接
  • ==
  • if语句和逻辑运算

字符串拼接

const a = 100 + 10 // 110
const b = 100 + '10' // '10010'
const c = true + '10' // 'true10'
复制代码

==

100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
复制代码

注:除了 == null 以外,其余都一概用 === ,例如:

const obj = { x: 100 }
if (obj.a == null) { }
// 至关于:
// if (obj.a === null || obj.a === undefined) { }
复制代码

if语句和逻辑运算

  • truly变量:!!a === true 的变量

  • falsely变量:!!a === false 的变量

truly变量

!!1 // true
!!{} // true
复制代码

falsely变量

!!0 // false
!!null // false
!!'' // false
!!undefined // false
!!NaN // false
!!false // false
复制代码

例子1:if 语句

// truly变量
const a = true
if (a) {
    // ...
}
const b = 100
if (b) {
    // ...
}

// falsely变量
const c = ''
if (c) {
    // ...
}

const d = null
if (d) {
    // ...
}

let e
if (e) {
    // ...
}
复制代码

例子2:逻辑判断

console.log(10 && 0) // 0
console.log('' || 'abc') // 'abc'
console.log(!window.abc) // true
复制代码

#原型和原型链

引例:

  1. 如何判断一个变量是否是数组?
  2. 手写一个简易的jQuery,考虑插件和扩展性。
  3. class的原型本质,怎么理解?

知识点:

  1. class和继承
  2. 类型判断instanceof
  3. 原型和原型链

class

  • constructor
  • 属性
  • 方法

例子:

// 类
class Student {
    constructor(name, number) {
        this.name = name
        this.number = number
        // this.gender = 'male'
    }
    sayHi() {
        console.log(`姓名 ${this.name} ,学号 ${this.number}`)
        // console.log(
        // '姓名 ' + this.name + ' ,学号 ' + this.number
        // )
    }
    // study() {
    // }
}

// 经过类 new 对象/实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()

const madongmei = new Student('马冬梅', 101)
console.log(madongmei.name)
console.log(madongmei.number)
madongmei.sayHi()
复制代码

继承

  • extends
  • super
  • 扩展或重写方法
// 父类
class People {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat something`)
    }
}

// 子类
class Student extends People {
    constructor(name, number) {
        super(name)
        this.number = number
    }
    sayHi() {
        console.log(`姓名 ${this.name} 学号 ${this.number}`)
    }
}

// 子类
class Teacher extends People {
    constructor(name, major) {
        super(name)
        this.major = major
    }
    teach() {
        console.log(`${this.name} 教授 ${this.major}`)
    }
}

// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()
xialuo.eat()

// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name)
console.log(wanglaoshi.major)
wanglaoshi.teach()
wanglaoshi.eat()
复制代码

类型判断 instanceof

xialuo instanceof Student // true
xialuo instanceof People // true
xialuo instanceof Object // true

[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
复制代码

原型

// class 其实是函数,可见是语法糖
typeof People // 'function'
typeof Student // 'function'

// 隐式原型和显示原型
console.log(xialuo.__proto__) // 隐式原型
console.log(Student.__proto__) // 显示原型
console.log(xialuo.__proto__ === Student.prototype) // true
复制代码

原型关系

  • 每一个class都有显示原型prototype
  • 每一个实例都有影视原型__proto__
  • 实例的__proto__指向对应class的prototype

示意图:

原型

基于原型的执行规则

  • 获取属性xialuo.name或执行方法xialuo.sayhi()时
  • 先在自身属性和方法寻找
  • 若是找不到,则自动__proto__去中查找。

原型链

console.log(Student.prototype.__proto__)
console.log(People.prototype)
console.log(People.prototype === Student.prototype.__proto__) // true
复制代码

示意图

原型链

原型链

引例解答:

如何判断一个变量是否是数组?

a instanceof Array
复制代码

手写一个简易的jQuery,考虑插件和扩展性。

class jQuery {
    constructor(selector) {
        const result = document.querySelectorAll(selector)
        const length = result.length
        for (let i = 0; i < length; i++) {
            this[i] = result[i]
        }
        this.length = length
        this.selector = selector
    }
    get(index) {
        return this[index]
    }
    each(fn) {
        for (let i = 0; i < this.length; i++) {
            const elem = this[i]
            fn(elem)
        }
    }
    on(type, fn) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
    // 扩展不少 DOM API
}

// 插件
jQuery.prototype.dialog = function (info) {
    alert(info)
}

// “造轮子”
class myJQuery extends jQuery {
    constructor(selector) {
        super(selector)
    }
    // 扩展本身的方法
    addClass(className) {

    }
    style(data) {
        
    }
}

// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))

复制代码

class的原型本质,怎么理解?

  • 原型和原型链的图示
  • 属性和方法的执行规则

#做用域和闭包

引例:

  • this的不一样应用场景,如何取值?
  • 手写bind函数
  • 实际开发中闭包的应用场景,举例说明
  • 建立10个a标签,点击的时候弹出对应的序号

知识点:

  • 做用域和自由变量
  • 闭包
  • this

做用域

做用域

分类

  • 全局做用域
  • 函数做用域
  • 块级做用域(ES6新增)
// ES6 块级做用域
if (true) {
    let x = 100 // 只有使用let或const等ES6特性才会触发块级做用域
}
console.log(x) // 会报错
复制代码

自由变量

  • 一个变量在当前做用域没有定义,但被使用了
  • 向上级做用,一层一层依次查找,直到找到为止

闭包

  • 做用域应用的特殊状况,有两种表现:
  • 若函数做为参数被传递
  • 函数做为返回值被返回

函数做为参数

// 函数做为参数被传递
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}
print(fn) // 100
复制代码

函数做为返回值

// 函数做为返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200
fn() // 100
复制代码

全部的自由变量的查找,是在函数定义的地方,向上级做用域查找,不是在执行的地方!!!

this

  1. 做为普通函数
  2. 使用call、apply、bind
  3. 做为对象方法被调用
  4. 在class方法中调用
  5. 箭头函数

this 取什么值,是在函数执行的时候肯定的,不是在函数定义的时候肯定的。该原则适用于以上5种状况。

例子1:普通函数及call、apply、bind中的this

function fn1() {
    console.log(this)
}

fn1() // window

fn1.call({ x: 100 }) // { x :100 }

const fn2 = fn1.bind({ x: 200 })
fn2() // { x : 200 }
复制代码

注:关于call,apply,bind的区别,见 JavaScript 中 call()、apply()、bind() 的用法

例子2:对象方法中的this

const zhangsan = {
    name: '张三',
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    wait() {
        setTimeout(function () {
            // this === window
            console.log(this)
        })
    }
}
复制代码

例子3:箭头函数中的this

const lisi = {
    name: '李四',
    sayHi() {
        // this 即当前对象
        console.log(this)
    },
    wait() {
        setTimeout(() => {
            // this === window
            console.log(this)
        })
    }
}
复制代码

例子4:class中的this

class People {
    constructor(name) {
        this.name = name
        this.age = 20
    }
    sayHi() {
        console.log(this)
    }
}
const zhangsan = new People('张三')
zhangsan.sayHi() // zhangsan 对象
复制代码

引例解答:

1. this的不一样应用场景,如何取值?

  • 做为普通函数
  • 使用call、apply、bind
    1. 做为对象方法被调用
  • 在class方法中调用
    1. 箭头函数

2. 手写bind函数

// 模拟 bind
Function.prototype.bind1 = function () {
    // 将参数拆解为数组
    const args = Array.prototype.slice.call(arguments)

    // 获取 this(数组第一项)
    const t = args.shift()

    // fn1.bind(...) 中的 fn1
    const self = this

    // 返回一个函数
    return function () {
        return self.apply(t, args)
    }
}

/* fn1.__proto__ === Function.prototype // true */

function fn1(a, b, c) {
    console.log('this', this)
    console.log(a, b, c)
    return 'this is fn1'
}

const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res)
复制代码

3. 实际开发中闭包的应用场景,举例说明

  • 隐藏数据
  • 如作一个简单的cache工具
// 闭包隐藏数据,只提供 API
function createCache() {
    const data = {} // 闭包中的数据,被隐藏,不被外界访问
    return {
        set: function (key, val) {
            data[key] = val
        },
        get: function (key) {
            return data[key]
        }
    }
}

const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
复制代码

4. 建立10个<a>标签,点击的时候弹出对应的序号

错误示例:❎

// 建立10个a标签,点击的时候弹出对应的序号
let i, a
for (i = 0; i < 10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function (e) {
        e.preventDefault()
        alert(i) // i 是全局做用域
    })
    document.body.appendChild(a)
}
复制代码

正确示例:✅

// 建立10个a标签,点击的时候弹出对应的序号
let a
for (let i = 0; i < 10; i++) {
    a = document.createElement('a')
    a.innerHTML = i + '<br>'
    a.addEventListener('click', function (e) {
        e.preventDefault()
        alert(i) // i 是块级做用域
    })
    document.body.appendChild(a)
}
复制代码

注:若将上面正确示例中for循环的let i = 0改成var i = 10,则其结果同错误示例,由于var定义的变量为全局变量。


(未完待续 ...)

#异步


#JS-Web-API-DOM


#JS-Web-API-BOM


#JS-Web-API-事件


#JS-Web-API-Ajax


#JS-Web-API-存储


#开发环境


#运行环境

参考资料: 初级JavaScript面试

相关文章
相关标签/搜索