Temporal Dead Zone(TDZ)
是 ES6(ES2015) 中对新作用域的专业语义。
在ES6
之前,JS的scope
只有两种,全局作用域和函数作用域,但是在ES6中出现了块级作用域,即使用let/const
可以定义块级作用域。
那么在ES6的新特性中,最容易看到TDZ作用的就是使用let/const的使用上面。let/const
与var
的主要不同有两个地方:
let/const
是使用区块作用域;var
是使用函数作用域let/const
声明之前就访问对应的变量与常量,会抛出ReferenceError
错误;但在var
声明之前就访问对应的变量,则会得到undefined
.1 | console.log(name1); // undefined; |
var
定义的变量时会得到undefined
,但是在使用let
未定义的变量时会抛出错误。因为ES6中的let声明的变量是不存在变量提升的作用。1 | var x = 10; |
let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。1 | if (true) { |
let
命令声明变量tmp
之前,都属于变量tmp
的死区。其实const
的行为和let
的行为差不多。只是const
定义的是常量,不变的数据。let
定义的是改变的数据。并且在声明const
的时候必须存在值。
1 | const name; //error |
对于const 需要明确一点,就是改变的是什么?看下面这个
1 | const person = { |
可以更改person.name
而不会导致错误,因为这会更改person
包含的内容,并且不会更改person
绑定的值。当此代码尝试为person
分配值从而尝试更改绑定时,将引发错误。const
如何与对象一起工作的这种微妙之处很容易被误解。 请记住:const阻止修改绑定,而不是阻止修改绑定值。
我们都知道使用typeof
可以用来检测给定变量的数据类型,也可以使用它来判断值是否被定义。当返回undefined
时表示值未定义;
但是在const/let
定义的变量在变量声明之前如果使用了typeof
就会报错
1 | typeof x; // ReferenceError |
因为x
是使用let
声明的,那么在x未声明之前都属于暂时性死区,在使用typeof
时就会报错。所以在使用let/const
进行声明的变量在使用typeof
时不一定安全。
1 | typeof y; // 'undefined' |
但是可以看出,如果我们只是用了typeof
而没有去定义,也是不会报错的,从这例可以看出只要没有明确规定使用const/let
定义之前就是不会出错。
例如下面一段代码,我们在使用
1 | function bar (x = y, y = 2) { |
上面代码中,调用bar
函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。如果y的默认值是x,就不会报错,因为此时x已经声明了。
1 | function bar(x = 2, y = x) { |
使用var和let声明的另外一种区别:
1 | // 不报错 |
受“死区”的影响,使用let
声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。
ES6
规定暂时性死区和let、const
语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在ES5
是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,
暂时性死区的本质
就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
注: TDZ最一开始是为了const所设计的,但后来的对let的设计也是一致的。
注: 在ES6标准中,对于const所声明的识别子仍然也经常为variable(变量),称为constant variable(固定的变量)。以const声明所创建出来的常量,在JS中只是不能再被赋(can’t re-assignment),并不是不可被改变(immutable)的,这两种概念仍然有很大的差异。
1 | var a = 10; |
答案:undefined
解释:使用var关键字声明的变量被提升并在内存中为其赋值为undefined.但是初始化恰好发生在你在代码中写入它们的地方。另外,var声明的变量是函数作用域,而let和const是块作用域。所以,这就是这个所从的样子:
1 | var a = 10; // 全局作用域 |
1 | var a = 10; |
答案:ReferenceError: a is not defined
解释:let和const允许你声明一个变量被限制在一个块级作用域,或语句或表达式中。不像var,这些变量不会被提升,并且具有所谓的temporal dead zone(TDZ)。尝试在TDZ中访问这些变量将抛出一个ReferenceError,因为它们只能在执行到达声明才可被访问。
1 | var a = 10; // 全局作用域 |
1 | var array = []; |
答案:[3,3,3]
解释:在for loop的头部声明一个带有var关键字的变量,为该变量创建一个绑定(存储空间)。
1 | // 误解作用域:认为存在块级作用域 |
如果你声明一个具有块级作用域的变量,则会为每个循环迭代创建一个新绑定。
1 | // 使用ES6块级作用域绑定 |
解决这个问题的另一种方法是使用闭包
1 | let array = []; |
为啥let可以,可以参考这篇文章
1 | function foo() { |
答案:不会
解释:JavaScript并发模型基于“事件循环”。当我说“浏览器是JS的家(归宿)”时,我真正的意思是浏览器提供运行时环境来执行我们的JavaScript代码。浏览器的主要组件包括调用堆栈 ,事件循环 ,任务队列 和 Web API。像setTimeout
,setInterval
和Promise
这样的全局函数不是JavaScript的一部分,而是Web API
的一部分。JavaScript环境的可视化表示如下所示:
JS调用堆栈是后进先出
(LIFO)
。引擎一次从堆栈中获取一个函数,并从上到下依次运行代码。每次遇到一些异步代码(如setTimeout)时,它都会将其交给Web API(箭头1)。因此,每当触发事件时,callback都会被发送到任务队列(箭头2)。Event Loop不断监视任务队列,并按照排队顺序一次处理一个callback。每当调用堆栈为空时,循环检索回调并将其放入堆栈(箭头3)进行处理。请记住,如果调用堆栈不为空,则事件循环不会将任何callbacks推送到堆栈。
现在,有了这些知识,让我们试着回答上述问题:
步骤
1.调用foo()
将把foo
函数放进调用栈。
2.在处理内部代码时,JS引擎遇到setTimeout
。
3.然后它将foo
回调移交给WebAPI(箭头1)并从函数返回。调用堆栈再次为空。
4.计时器设置为0
,因此foo
将被发送到任务队列(箭头2)。
5.因为,我们的调用堆栈是空的,事件循环将选择foo回调
并将其推送到调用堆栈进行处理。
6.进程再次重复,堆栈不会溢出 。
1 | function foo() { |
答案:不会
解释:大多数时候,我看到开发人员假设在事件循环的蓝图中只有一个任务队列(笔: 也叫task queue
或event queue
或callback queue
)。但事实并非如此。我们可以有多个任务队列。由浏览器选择任意的队列并在其中处理callbacks。
在高层次上来看,JavaScript中有宏任务和微任务。setTimeout
回调是macrotasks
而Promise
回调是 microtasks
。主要的区别在于他们的执行仪式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行返回到event loop
(包括任何额外排队的项)之前清空。因此,如果你将这些项快速的添加到这个你正在处理的队列,那么你将永远在处理微任务。
在执行返回事件循环之前,微任务队列总是被清空
现在,当你在控制台中运行以下代码段时:
1 | function foo() { |
每次调用’foo’都会继续在微任务队列上添加另一个’foo’回调,因此事件循环无法继续处理其他事件(scroll
,click
等),直到该队列完全清空为止。因此,它会阻止渲染。
1 | var obj = { x: 1, y: 2, z: 3 }; |
答案:可以, 通过是对象iterables
解释:拓展运算符和for-of
语句迭代iterable
对象。数组或Map是具有默认迭代行为的内置iterable
。对象不是可迭代的,但你可以使用iterable
和iterator协议
使它们可迭代。
在Mozilla文档中,如果一个对象实现了@@iterator方法,那么它就是可迭代的,这意味着这个对象(或者它原型链上的一个对象)必须有一个带有@@iterator键的属性,这个键可以通过常量Symbol.iterator获得。
上述陈述可能看起来有点冗长,但下面的例子会更有意义:
1 | var obj = { x: 1, y: 2, z: 3 }; |
你还可以使用generator函数来自定义对象的迭代行为:
1 | var obj = { x: 1, y: 2, z: 3 }; |
1 | var obj = { a: 1, b: 2 }; |
Object.setPrototypeOf
方法是针对对象实例的,而不是构造函数(类),此方法修改的是对象实例的内部属性[prototype]
,也就是_proto_
属性所指向的对象,它只是修改了特定对象上的原型对象,对于构造函数的prototype
指向的原型对象没有影响。那是不是此方法就不能针对构造函数了,因为构造函数本身也是function(类)的实例。
语法结构:
Obejct.setPrototypeOf(obj,proto);
参数解析:
(1).obj:必需,对其设置原型的对象。
(2).proto:必需,新的原型对象。
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法结构:
Object.defineProperty(obj, prop, descriptor);
obj:要定义属性的对象。
prop:要定义或修改的属性的名称或 Symbol 。
descriptor:要定义或修改的属性描述符。
返回值–>被传递给函数的对象。
答案:a,b,c
解释: for-in
循环遍历对象本身的可枚举属性以及对象从其原型继承的属性。可枚举属性是可以在for-in
循环期间包含和访问的属性。
1 | var obj = { a: 1, b: 2 }; |
现在掌握了这些知识,应该很容易理解为什么我们的代码会打印出这些特定的属性:
1 | var obj = { a: 1, b: 2 }; // a, b are both enumerables properties |
1 | var x = 10; |
答案:10
解释:当我们将x初始化为全局作用域时,它将成为window对象的属性(假设它是浏览器环境而不是严格模式)。看下面代码:
1 | var x = 10; // 全局作用域 |
我们可以断言:
1 | window.x === 10; // true |
this将始终指向调用该方法的对象。因此,在foo.getX()
的情况下,this
指向foo
对象返回值90。而在xGetter()
的情况下,this
指向window
对象返回值10。
要检索foo.x
的值,我们可以通过使用Function.prototype.bind
将 this
的值绑定到foo
对象来创建新函数。
1 | let getFooX = foo.getX.bind(foo); |
作者:xiaohesong
链接:https://www.jianshu.com/p/aec200057e33
来源:简书
1.User Interface
用户界面,我们所看到的浏览器
2.Browser engine
浏览器引擎,用来查询和操作渲染引擎
3.Rendering engine
渲染引擎,用来显示请求的内容,负责解析HTML、CSS,并把解析的内容显示出来
4.Networking
网络,负责发生网络请求
5.javaScript Interpreter
(解析者) javaScript
解析器,负责执行javaScript
的代码
6.UI Backend
UI后端,用来绘制类似组合框和弹出窗口
7.Data Persistence
(持久化) 数据持久化,数据存储 cookie、HTML5中的sessionStorage
javaScript运行分为两个阶段:
*预解析
-全局预解析(所有变量和函数声明都会提前:同名的函数和变量函数的优先级高)
-函数内部预解析(所有的变量、函数和形参都会参与预解析)
-函数
-形参
-普通变量
*执行
先预解析全局作用域,然后执行全局作用域中的代码
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后执行函数内代码。
面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。
面向对象编程————Object Oriented Programming
,简称OOP
,是一种编程开发思想.
面向对象与面向过程:
面向对象的特性:
- JSON是JS的一种简单数据格式,JSON是JavaScript原生格式,它是一种严格的js对象的格式
- JSON的属性名必须有双引号,如果值是字符串,也必须是双引号
JSON只是一种数据格式
1 | var obj = {}; //这只是JS对象 |
区别 | JSON | JavaScript对象 |
---|---|---|
含义 | 仅仅是一种数据格式 | 表示类的实例 |
传输 | 可以跨平台数据传输,速度快 | 不能传输 |
表现 | 1.键值对方式,键必须加双引号 2.值不能是方法函数,不能是undefined/NaN | 1.键值对方式,键不加引号 2.值可以是函数、对象、字符串、数字、boolean 等 |
相互转换 | Json转换Js对象1.JSON.parse(JsonStr); (不兼容IE7) 2.eval(“(“+jsonStr+”)”); (兼容所有浏览器,但不安全,会执行json里面的表达式?) | js对象转换JsonJSON.stringify(jsObj); |
序列化选项:
JSON.stringify()
:把一个js对象转换为JSON字符串
要接受的参数除了js对象外,还可以接收两个参数:
1 | const book = { |
参数为一个数组时
1 | const jsonText = JSON.stringify(book, ["title", "year"]); |
输出结果为: {"title":"web前端基础","year":2020}
参数为函数时
传入的函数接收两个参数,属性(键)名和属性值。根据属性(键)名可以知道应该如何处理要序列化的对象中的属性,属性名只能是字符串,而在值并非键值对儿结构的值时,键名可以是空字符串为了改变序列化对象的结果,函数返回的值就是相应键的值。不过要注意,如果函数返回了undefined,那么相应的属性就会被忽略。
1 | const jsonText2 = JSON.stringify(book, function(key, value) { |
输出结果为:{"title":"web前端基础","authors":"csgo-rush-b","year":"2020年出版"}
如果这个参数是一个数值,那它表示的是每个级别缩进的空格数。最大缩进空格为10,大于10会自动转换为10
1 | const jsonText3 = JSON.stringify(book, null, 4); |
输出结果为:
1 | { |
如果缩进参数是一个字符串而非数值,则这个字符串将在JSON字符串中被用作缩进字符(不再使用空格)超过10个,自动转换为10
1 | const jsonText4 = JSON.stringify(book, null, "---"); |
输出结果为:
1 | { |
有时候
JSON.stringify()
还是不能满足对某些对象进行自定义序列化的需求。可以使用toJSON()
,返回其自身的JSON数据格式。
1 | const book2 = { |
输出结果为:
1 | "js高级程序设计(第三版)" |
第一种:
# 一级标题
## 二级标题
### 三级标题
...
注意:#号后面有空格
第二种:
这是一级标题
===============
这是二级标题
---------------
注意:这种方式好像只能表示以及和二级,而且=和-没有限制
第三种:
# 一级标题 #
## 二级标题 ##
### 三级标题 ###
...
无序列表:
+ 1
- 2
* 3
可以看到,无序列表可以使用 * , + , - ,来创建
有序列表:
3. 列表1
2. 列表2
1. 列表3
有序列表就相对简单一点,只有这一种方式,注意,数字后面的点只能是英文的点
比如说,你想对某个部分做的内容做一些说明或者引用某某的话等,可以用这个语句
* 我可是吃了迷路果实的剑客
> 罗罗诺亚.索隆
罗罗诺亚.索隆
> 一级引用
>> 二级引用
>>> 三级引用
>>>> 四级引用
一级引用
二级引用
三级引用
四级引用
分割线可以由 * , -, _ ,这三个符号组成,且至少3个符号表示,而且不需要连续,有空格也行。
***
***********
-------
- - - -
******
* * ** * *
支持两种链接方式: 行内式和参数式,不管是哪一种,链接文字都是用 [方括号] 来标记
RGB是什么意思
链接还可以带title属性:左[RGB】(http://www.baidu.com “三原色”)
RGB—移动鼠标至此可以看到title
[RGB]: http://www.baidu.com "三原色"
[RGBA]: http://www.baidu.com "A代表透明度"
这里是[RGB],这里是[RGBA]
图片也有两种方式:行内式和参数式
用法跟链接的基本一样,唯一的不同就是,图片前面要写一个!
如果代码量比较少,只有单行的话,可以用单反引号包起来,多行用三个反引号,如果要写注释,可以在反引号后面写.
<p><a href="/Notes" title="技术栈">主要技术栈</a></p>
1 | <p><a href="/Notes" title="技术栈">主要技术栈</a></p> |
| name | age | sex |
|:------:|:-----|------:|冒号写左边,左对齐,写右边,右对齐
| tony | 20 | 男 |
| lucy | 18 | 女 |
name | age | sex |
---|---|---|
tony | 20 | 男 |
lucy | 18 | 女 |
一个星号或者是一个下划线包起来,会转换为<em>
倾斜,如果是2个,会转换为<strong>
加粗
字体倾斜
字体倾斜
字体加粗
字体加粗
* \\
* \`
* \~
* \*
* \_
* \!
~~删除线~~
删除线
React为了提升渲染方面的性能,在内部维持了一个虚拟dom(virtul DOM),当渲染结构有所变化的时候,会在虚拟dom中先用diff算法先进行一次对比,将所有的差异化解决之后,再一次性根据虚拟dom的变化,渲染到真实的dom结构.
关于key属性的使用,涉及到diff算法中同级节点的对比策略,当我们使用指定的key值时,key值会作为当前组件的id,diff算法会根据这个id来进行匹配。如果遍历新的dom结构时,发现组件的id在旧的dom结构中存在,那么React会认为当前组件只是位置发生了变化,因此不会将旧的组件销毁重新创建,只会改变当前组件的位置,然后再检查组件的属性有没有发生变化,然后选择保留或修改当前组件的属性。如果没有显式指定key的值,React会把当前组件数据源的index作为默认的key值.
首先,我们要确保key值的唯一,事实上如果key值不唯一的话,React只会渲染第一个,剩下的React会认为是同一项,直接忽略。其次,尽量避免使用index值作为组件的key值,使用index作为key不能保证一定是唯一的.
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true