🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 变量类型和计算 #### 值类型、引用类型 ```javascript // 值类型 let a = 100 let b = a a = 200 console.log(b) // 100 // 引用类型 let c = { age: 18 } let d = c d.age = 20 console.log(c) // {age:20} ``` > 值类型存储于 **栈** > > 引用类型存储于 **堆** > > 应用类型存储的是内存地址,d = c,表示d和c指向同一块内存 ```javascript // 常见引用类型 const obj = { x:100 } const arr = ['a','b','c'] const n = null // 特殊引用类型,指针指向为空指针 // 特殊引用类型,但不用于存储数据,所以没有拷贝,复制函数 function fun() {} ``` #### typeof * 识别所有值类型 * 识别函数 * 判断是否是引用类型(不可再细分、**不能区分数组、对象、null**) ```javascript // 区分数组 和 对象 [] instanceof Array --> true ``` ![image-20210302104739491](https://image.mdashen.com/pic/image-20210302104739491.png) #### 深拷贝 ```javascript /** * 深拷贝 * @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 } ``` #### 变量计算-类型转换 ##### 字符串拼接 ```javascript 100 + 10 // 110 number 100 + '10' // '10010' string true + '1' // 'true1' 字符串 true + 1 // 2 number ``` ##### == 运算符 ```javascript 100 == '100' // true 0 == '' // true 0 == false // true false == '' // true null == undefined // true null == false // false ``` ```javascript // 日常使用中 除了 == null 之外,其他都一律使用 === const obj = { x: 100 } if (obj.a == null) { // 相当于 if (obj.a === null || obj.a === undefined) } ``` ##### truly变量、falsely变量 * truly 变量:!!a === true 的变量 * false 变量:!!a === false 的变量 ```javascript // 举例、1 两次去反、1 是 truly 变量 !1 = false !!1 = !false => ture ``` ```javascript // 以下是 falsely 变量。除此之外都是 truly 变量 !!0 === false !!NaN === false !!'' === false !!null === false !!undefined === false !!false === false ``` * if 语句判断就是 truly 变量;如 `if('')1;else 0; // 0` ### 原型和原型链 #### 类型判断--instanceof ```javascript // Object 是所有类的父类,基类 class People { // …… } class Student extends People { // …… } const xialuo = new Student() console.log(xialuo instanceof Student) // true console.log(xialuo instanceof People) // true console.log(xialuo instanceof Object) // true console.log([] instanceof Array) // true console.log([] instanceof Object) // true console.log([] instanceof Object) // true ``` #### 原型--隐士原型、显示原型 ```javascript // class 实际上是函数 ,语法糖 typeof People // 'function' console.log( xialuo.__proto__) // 隐式原型 console.log( Student.prototype ) // 显示原型 xialuo.__proto__ === Student.prototype // ture ``` ![image-20210303102314479](https://image.mdashen.com/pic/image-20210303102314479.png) * 每个 class 都有显示原型 `prototype` * 每个实例都有隐式原型 `__proto__` * 实例的 `__proto__` 执行对应 class 的 `prototype` #### 基于原型和执行规则 * 获取属性 xialuo.name 或执行方法 xialuo.sayhi() 时 * 先在自身属性和方法寻找 * 找不到,去 `__proto__` 总查找 #### 原型链 ```javascript // class Student 是 class People 的子类 People.prototype === Student.prototype.__proto__ ``` ![image-20210303103401585](https://image.mdashen.com/pic/image-20210303103401585.png) ```javascript xialuo.hasOwnProperty('name') // true xialuo.hasOwnProperty('sayHi') // false xialuo.hasOwnProperty('hasOwnProperty') // false ``` #### instanceof 原理 ```javascript xialuo instanceof People // true // 按照上图 xialuo 的原型链,去找 People 的显示原型 prototype 能找到 true ``` #### 提示 * class 是 ES6 语法规范,由 ECMA 委员会发布 * ECMA 只规定语法规则,即只规定代码的书写规范,不规定如何使用 * 以上实现方式 为 V8 引擎的实现方式,也是主流实现方式 ### 作用域和闭包 #### 作用域、自由变量 ```javascript // 创建 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) }) document.body.appendChild(a) } ``` ![image-20210303115729445](https://image.mdashen.com/pic/image-20210303115729445.png) * 全局作用域 * 函数作用域 * 块级作用域(ES6新增) ##### 自由变量 * 一个变量在当前作用域没有定义,但被使用了 * 向上级作用域,一层一层一次寻找,指导找到为止 * 如果找到全局作用域都没找到,则报错 xx is not defined #### 闭包 * 作用域应用的特殊情况,有两种表现 * 函数作为参数被传递 * 函数作用返回值被返回 ```javascript // 函数作为返回值 function create() { let a = 100 return function () { console.log(a) } } let a = 200 let fn = create() fn() // 100 // create()() ``` ```javascript // 函数作为参数 function print(fnn) { let b = 200 fnn() } let b = 100 function fnn() { console.log(b) } print(fnn) // 100 ``` > 闭包:自由变量从函数定义的地方开始找,依次向上级作用域查找。 ##### 闭包的实际应用 * 隐藏数据 ```javascript 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) // 在外部无法直接访问data中的数据 // console.log(data.a) // data is not defined console.log(c.get('a')) ``` #### this 赋值问题 * 常见情况 * 作为普通函数 * 使用 call apply bind * 作为对象方法被调用 * 在 class 方法中调用 * 箭头函数 * **this 在各个场景中取值:函数执行的时候确定的,不是函数定义的时候确认的** ```javascript function fn1() { console.log(this) } fn1() // window fn1.call({ x:100 }) //{ x: 100 } const fn2 = fn1.bind({ x: 200 }) fn2() // {x: 200} ``` ![image-20210303123535183](https://image.mdashen.com/pic/image-20210303123535183.png) ##### call apply bind 简介-区别 > 改变函数执行时的上下文,即改变函数运行时的 this 指向 * call 和 apply ```javascript obj.call(thisObj, arg1, arg2, ...); obj.apply(thisObj, [arg1, arg2, ...]); ``` > 两者作用一致,都是把`obj`(即this)绑定到`thisObj`,这时候`thisObj`具备了`obj`的属性和方法。或者说`thisObj`『继承』了`obj`的属性和方法。绑定后会立即执行函数。 > > 唯一区别是**apply接受的是数组参数**,**call接受的是连续参数**。 bind的使用 ```javascript obj.bind(thisObj, arg1, arg2, ...); ``` > 把obj绑定到thisObj,这时候thisObj具备了obj的属性和方法。与call和apply不同的是,bind绑定后不会立即执行。需要调用