可以看这里:1. 彻底搞懂javascript-词法环境(Lexical Environments)、词法环境是什么?、js 图解变量环境、词法环境、执行上下文,作用域链查找过程
执行上下文一般会关联两个词法环境:
let
、const
声明的标识符var
、function
声明的标识符两种环境记录值:声明式环境记录和对象环境记录。
let:用于声明一个变量。
const:
let、const在同一个作用域里面, 不允许重复声明变量。
var声明的变量会作用域提升:在声明之前可以访问, 只不过值是undefined
console.log(foo); // undefined var foo = "foo";
'运行
而let、const声明的变量,在声明之前访问会报错:let、const定义的变量不是当代码执行到那一行才创建出来的, 在这之前就已经创建出来, 只是不能访问。
省流:let、const没有作用域提升,但是会在解析阶段被创建出来。只是创建出来后、代码执行到那一行之前不能访问。
已知在let、const定义的标识符在执行到声明代码前是不能被访问的。
从块作用域的顶部一直到变量声明完成之前,这个变量处在的区域就是暂时性死区(TDZ,temporal dead zone)。
暂时性死区与定义的位置无关, 与代码的执行顺序有关:若与定义的位置有关,则这里应该是undefined。
function foo(){ console.log(address); } let address="address" foo()//address
'运行
暂时性死区形成后, 在该区域内这个标识符不能访问:
如下面代码中, foo作用域下会优先访问自己作用域下的address, 而foo作用域中, 执行到第7行代码之前, 已经形成暂时性死区, 所以不能访问, 就会报错。
let address="window address"
function foo(){
console.log(address);
let address="foo address"
}
foo()
已知:在全局通过var来声明一个变量,事实上会在window上添加一个属性。
但是:let、const是不会给window上添加任何属性的。
let message="message"
const address="address"
console.log(window.message);//undefined
console.log(window.address);//undefined
ES6之前,JavaScript只会形成两个作用域: 全局作用域和函数作用域。在ES6中新增了块级作用域,并且通过let
、const
、function
、class
声明的标识符是具备块级作用域的限制的:
let
{
let message="let"
}
console.log(message);//Uncaught ReferenceError: message is not defined
const
{
const message="const"
}
console.log(message);//Uncaught ReferenceError: message is not defined
class
{
class Person{}
}
var p=new Person()//Uncaught ReferenceError: Person is not defined
但是:函数拥有块级作用域,外面依然可以访问的——JS引擎会对函数的声明进行特殊的处理,允许像var那样进行提升
{ function foo(){ console.log("foo"); } } foo()//foo
'运行
var:
let vs const:
ES6允许我们使用字符串模板来嵌入JS的变量或者表达式来进行拼接:
动态嵌入内容:
const name="kaisa" const age=18 console.log(`name is ${name},age is ${age}`);//name is kaisa,age is 18
'运行
拼接表达式:
const age=18 console.log(`我是成年人吗?${age>=18?'是':'不是'}`);//我是成年人吗?是
'运行
接收函数的返回值:
function foo() { return "foo"; } console.log(`${foo()}`); //foo
'运行
如果我们使用标签模板字符串调用函数 ,并且在调用的时候插入其他的变量:
const name="kaisa" const age=18 function foo(...args){ console.log(args); } foo`name is ${name},age is ${age}` //[Array(3), 'kaisa', 18] //Array(3):['name is ', ',age is ', '']
'运行
// x的默认值为20 y的默认值为30 function foo(x = 20, y = 30) { console.log(x, y); } foo(); // 20 30 foo(10, 50); // 10 50
'运行
注意:
function foo({name,age}={name:"kaisa",age:18}){ console.log(name,age); } foo()//kaisa 18
'运行
function foo({name="kaisa",age=18}={}){ console.log(name,age); } foo()//kaisa 18
'运行
箭头函数没有显式原型prototype,所以不能作为构造函数,使用new来创建对象。
如下:
const foo=()=>{} console.log(foo.__proto__===Function.prototype);//true console.log(foo.prototype)//undefined
'运行
展开运算符其实是一种浅拷贝
函数调用时的使用:
const names=['a','b','c','d'] function foo(arg,...args){ console.log(arg,args); } foo(...names);//a (3) ['b', 'c', 'd']
'运行
const str="hello" function foo(arg,...args){ console.log(args); } foo(...str);//(4) ['e', 'l', 'l', 'o']
'运行
数组构造时使用:
const names = ["aaa", "bbb", "ccc"]; const newNames = [...names, "ddd", "eee"] console.log(newNames) // ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
'运行
构建对象字面量时使用:
const obj = { name: "kaisa", age: 18 } const info = { ...obj, height: 1.88, address: "成都市" } console.log(info) // {name: 'kaisa', age: 18, height: 1.88, address: '成都市'}
'运行
// 十进制 const num1 = 100 // 二进制 const num2 = 0b100 // 八进制 const num3 = 0o100 // 十六进制 const num4 = 0x100
'运行
数字过长时,可以使用_作为连接符:
const num5 = 100_000_000
'运行
是基本数据类型,翻译为符号。
为什么要有Symbol:防止造成属性名的冲突。如:有一个对象,我们希望在其中添加一个新的属性和值,但我们在不确定它原来内部有什么内容的情况下,很容易造成冲突,从而覆盖掉它内部的某个属性。Symbol的出现,就是为了防止这种冲突的。
const s1=Symbol() const obj={ [s1]:'a' }; console.log(obj);//{Symbol(): 'a'}
'运行
Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的。
const s1=Symbol() const s2=Symbol() const obj={ [s1]:'a', [s2]:'b' }; console.log(obj);//{Symbol(): 'a', Symbol(): 'b'} console.log(s1===s2);//false
'运行
我们通常会使用Symbol在对象中表示唯一的属性名。我们有以下几种写法。
1.属性名赋值
const s1 = Symbol(); const s2 = Symbol(); const obj = {}; obj[s1] = "aaa"; obj[s2] = "bbb";
'运行
2.定义字面量直接使用
const s1 = Symbol(); const s2 = Symbol(); const obj = { [s1]: "aaa", [s2]: "bbb" };
'运行
3.Object.defineProperty
const s1 = Symbol(); const s2 = Symbol(); const obj = {}; Object.defineProperty(obj, s1, { value: "aaa", }); Object.defineProperty(obj, s2, { value: "bbb", });
'运行
我们获取对象的key的方法, 无法获取Symbol的key:
const s1 = Symbol(); const s2 = Symbol(); const obj = { name: "kaisa", age: 18, [s1]: "aaa", [s2]: "bbb", }; console.log(Object.keys(obj)); // ['name', 'age']
'运行
不过,可以通过Object.getOwnPropertySymbols()
方法:
const s1 = Symbol(); const s2 = Symbol(); const obj = { name: "kaisa", age: 18, [s1]: "aaa", [s2]: "bbb", }; console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(), Symbol()]
'运行
ES10新增了特性:description
。
const s1=Symbol('des') console.log(s1.description);//des
'运行
创建相同的Symbol的方法:Symbol.for(key)
const s1=Symbol.for('a') const s2=Symbol.for('a') console.log(s1===s2);//true
'运行
获取对应的key:Symbol.keyFor
console.log(Symbol.keyFor(s1));//a
ES6中新增了两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。
可以用来保存数据,类似于数组,但是元素不能重复。
创建Set我们需要通过Set 构造函数
const set = new Set(); console.log(set); // Set(0) {size: 0}
'运行
Set中存放的元素是不会重复的,所以可以数组去重:
const set=new Set(); console.log(set);//Set(0) {size: 0} set.add(1) set.add(1) set.add(1) set.add(2) console.log(set);//Set(2) {1, 2}
'运行
属性:
方法:
const set=new Set(); set.add(1) set.add(10) set.add(100) set.forEach((item)=>{ console.log(item+1);//2 11 101 })
'运行
set.add(1)
set.add(10)
set.add(100)
for(item of set){
console.log(item+1);//2 11 101
}
与Set的区别:
WeakSet常见的方法:
当我们不想通过非构造方法创建出来的对象来调用类方法时,可以使用weakSet:
使用前:
class Person { eating() { console.log(this, "在eating~") } } const p = new Person() p.eating()//Person {} 在eating~ p.eating.call({ name: "kaisa" }) //{ name: 'kaisa' } 在eating~ const newEating = p.eating.bind("aaa") newEating() //aaa 在eating~
'运行
使用后:
const personSet = new WeakSet() class Person { constructor(){ personSet.add(this) } eating() { if(!personSet.has(this)){ throw new Error("不能通过非构造方法创建出来的对象调用running方法") } console.log(this, "在eating~") } }
'运行
Map,用于存储映射关系。对象也可以存储映射关系,它们的区别是:
属性:
方法:
const key1={name:"name1"}; const key2={age:"18"} const map=new Map() map.set(key1,"a") map.set(key2,"asd") map.forEach(item=>{ console.log(item);//a asd })
'运行
const key1={name:"name1"}; const key2={age:"18"} const map=new Map() map.set(key1,"a") map.set(key2,"asd") for(item of map){ console.log(item);//[{…}, 'a'] [{…}, 'asd'] }
'运行
const key1={name:"name1"}; const key2={age:"18"} const map=new Map() map.set(key1,"a") map.set(key2,"asd") for(item of map){ const[key,value]=item console.log(key);//{name: 'name1'} {age: '18'} }
'运行
与Map的区别:
常见方法:
应用:
coderwhy的课
ES6-ES12部分简单知识点总结,希望对大家有用~
ES6常见的新特性(超详细)、let/const、模板字符串、ES6函数增强、Symbol数据类型、set/map数据结构详细介绍
1. 彻底搞懂javascript-词法环境(Lexical Environments)
词法环境是什么?
js 图解变量环境、词法环境、执行上下文,作用域链查找过程
ES6-12