var、let和const
一、var
变量提升
例一
console.log(a)
var a=1;
↓
var a;
console.log(a) // undefined
a = 1;
例二
var a=10;
var a;
console.log(a)
↓
var a;
var a;
var a=10;
console.log(a) // 10
函数声名提升
console.log(a(5))
function a(x) {
return x*x
}
↓
function a(x) {
return x*x
}
console.log(a(5)) //25
var的特点
- var声名的变量会被提升到作用域的顶部
- 在全局作用域下的var定义的白能量会被挂载到
windows
上,而let
和const
不会
二、let和const
暂时性死区
function test(){
console.log(a)
let a = 10
}
test() // 报错
原因:暂时性死区
解释:对于let和const定义的变量,不能在声明前使用。虽然变量在编译的环节中被告知这块作用域可以访问(提升),但是访问是受限的。
三、区别总结
- 函数提升优于变量提升,函数提升会把整个函数挪到作用域顶部。而变量提升只会把变量声名放到作用域顶端,变量的定义依旧会放后
- let和const因为暂时性死区,访问受限。不能在声明前使用
- 在全局作用域声名,var会挂载到windows对象上,而let,const不会
- let和const的区别是const不能再次赋值
原型继承和Class继承
原型继承
普通继承
function Person() {}
Person.prototype.name = "awsl"
person1 = new Person();
console.log(person1.name) // awsl
组合继承
在子类的构造函数中通过Parent.call(this)
继承父类的属性,然后改变子类的原型为 new Parent()
来继承父类
的函数。
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
继承组合继承
这种继承方式对组合继承进行了优化,组合继承缺点在于继承父类函数时调用了构造函数,我们只需要优化掉这点就行了。
function parent(value) {
this.val = value
}
parent.prototype.getValue = function() {
console.log(this.value)
}
function child(value) {
parent.call(this,value)
}
child.prototype = Object.create(parent.prototype,{
c constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
Class继承
class parent {
constructor(value){
this.val = value
}
getValue(){
console.log(this.val)
}
}
class child extends parent {
constructor(value){
super(value) // 类似 parent.call(this,value)
this.val = value
}
}
let test = new child(1)
test.getValue() // 1
child instanceof parent //true
class
实现继承的核心在于使用 extends
表明继承自哪个父类,并且在子类构造函数中必须调用super
,因为这段代码可以看成Parent.call(this,value)
。
当然了,之前也说了在 JS 中并不存在类,class 的本质就是函数
模块化
为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?
使用模块化的原因
解决命名冲突
提供复用性
提高代码可维护性
一、立即执行函数
(function(g){
g.test = function(){}
// 可以各种声名变量,函数都不会污染全局作用域
})
二、AMD 和 CMD
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使用
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地方实现延迟加载
var a = require('./a')
a.doSomething()
})
三、CommonJS
// a.js 中
moudule.exports = {
a:1
}
//or
exports.a = 1
//b.js文件中
var moudule = require('./a.js')
moudule.a // ->log 1
require
var module = require('./a.js')
module.a
// 这里其实就是包装了一层立即执行函数,这样就不会污染全局变量了,
// 重要的是 module 这里,module 是 Node 独有的一个变量
module.exports = {
a: 1
}
// module 基本实现
var module = {
id: 'xxxx', // 我总得知道怎么去找到他吧
exports: {} // exports 就是个空对象
}
// 这个是为什么 exports 和 module.exports 用法相似的原因
var exports = module.exports
var load = function (module) {
// 导出的东西
var a = 1
module.exports = a
return module.exports
};
// 然后当我 require 的时候去找到独特的
// id,然后将要使用的东西用立即执行函数包装下,over
ES Module
ES Module 和 CommonJS的区别
- CommonJS 支持动态导入,后者不支持
- CommonJS 是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。后者是异步导入,因为用于浏览器,需要下载文件,固采取异步
- CommonJS在导出时都是值拷贝,互不影响。如果想更新值必须重新导入一次。ES Module采取实时绑定的方式,导入导出的值都指向同一个内存地址,导入值会随着导出值变化
- ES Module 会编译成
require/exports
来执行
// 引入模块 API
import XXX from './a.js'
import {XXX} from './a.js'
// 导出模块 API
export function a() {}
export default function() {}
Proxy
Proxy可以实现什么功能
Proxy是ES6中新增的功能,可以用来自定义对象中的操作
在 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式
let p = new Proxy(target,handler)
// target : 代表添加代理的对象
// handler: 用来自定义对象中的操作,比如自定义`set`或者`get`函数
通过Proxy
来实现一个数据响应式
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, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 监听到属性a改变
p.a // 'a' = 2
map ,filter和reduce
map
生成一个新数组,遍历原数组,将每个元素拿出来做一些变换后放入新数组
let arr = [1,2,3].map(v => v+1) // -> [2,3,4]
map的回调函数接受三个参数:当前索引元素,索引,原数组
filter
新建一个数组,通过回调函数设置条件,将返回值为True的元素放入新数组。我们可以利用这个来过滤数组元素
let arr = [1,2,3,4,5,6].filter(item => item!==6)
console.log(arr) // [1,2,3,4,5]
回调函数接受三个参数:当前索引元素,索引,原数组
reduce
reduce可以通过回调函数,把数组中的元素转化为一个值
// 累加
let arr=[1,2,3],sum=0;
for(let i=0;i<arr.length;i++){
sum += arr[i]
}
↓
let sum = arr.reduce((acc,cur) => pre+cur,0)
reduce有两个参数,回调函数和初始值
回调函数的参数:累计值(从初始值开始的每一轮运算结果),当前元素,当前索引,原数组
← 查漏补缺——ES基础知识(二) JS进阶 →