这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

1. let、const、var的区别

(1)块级作用域:块作用域由 { }包括,let和const具有块级作用域,var不存在块级作用域。块级作用域解决了ES5中的两个问题:

  • 内层变量可能覆盖外层变量
  • 用来计数的循环变量泄露为全局变量

(2)变量提升:var存在变量提升,let和const不存在变量提升,即在变量只能在声明之后使用,否在会报错。

(3)给全局添加属性:浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。

(4)重复声明:var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。

(5)暂时性死区:在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。

(6)初始值设置:在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。

(7)指针指向:let和const都是ES6新增的用于创建变量的语法。 let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。

2. const对象的属性可以修改吗

const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。对于基本类型的数据(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。

但对于引用类型的数据(主要是对象和数组)来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。

3. 如果new一个箭头函数的会怎么样

箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。

new操作符的实现步骤如下:

  1. 创建一个对象
  2. 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性)
  3. 指向构造函数中的代码,构造函数中的this指向该对象(也就是为这个对象添加属性和方法)
  4. 返回新的对象

所以,上面的第二、三步,箭头函数都是没有办法执行的。

4. 箭头函数与普通函数的区别

(1)箭头函数比普通函数更加简洁

  • 如果没有参数,就直接写一个空括号即可
  • 如果只有一个参数,可以省去参数的括号
  • 如果有多个参数,用逗号分割
  • 如果函数体的返回值只有一句,可以省略大括号
  • 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常见的就是调用一个函数:
let fn = () => void doesNotReturn();

(2)箭头函数没有自己的this

箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。

(3)箭头函数继承来的this指向永远不会改变

var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
new obj.a() // undefined
new obj.b() // Uncaught TypeError: obj.b is not a constructor

对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。需要注意,定义对象的大括号{}是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。

(4)call()、apply()、bind()等方法不能改变箭头函数中this的指向

var id = 'Global';
let fun1 = () => {
console.log(this.id)
};
fun1(); // 'Global'
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'

(5)箭头函数不能作为构造函数使用

构造函数在new的步骤在上面已经说过了,实际上第二步就是将函数中的this指向该对象。 但是由于箭头函数时没有自己的this的,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。

(6)箭头函数没有自己的arguments

箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。

(7)箭头函数没有prototype

(8)箭头函数不能用作Generator函数,不能使用yeild关键字

5. 箭头函数的this指向哪⾥?

箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于⾃⼰的this,它所谓的this是捕获其所在上下⽂的 this 值,作为⾃⼰的 this 值,并且由于没有属于⾃⼰的this,所以是不会被new调⽤的,这个所谓的this也不会被改变。

可以⽤Babel理解⼀下箭头函数:

// ES6
const obj = {
getArrow() {
return () => {
console.log(this === obj);
};
}
}

转化后:

// ES5,由 Babel 转译
var obj = {
getArrow: function getArrow() {
var _this = this;
return function () {
console.log(_this === obj);
};
}
};

6. 扩展运算符的作用及使用场景

(1)对象扩展运算符

对象的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。

let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }

上述方法实际上等价于:

let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性)。

同样,如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。

let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}

利用上述特性就可以很方便的修改对象的部分属性。在redux中的reducer函数规定必须是一个纯函数reducer中的state对象要求不能直接修改,可以通过扩展运算符把修改路径的对象都复制一遍,然后产生一个新的对象返回。

需要注意:扩展运算符对对象实例的拷贝属于浅拷贝

(2)数组扩展运算符

数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。

console.log(...[1, 2, 3])
// 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5

下面是数组的扩展运算符的应用:

  • 将数组转换为参数序列
function add(x, y) {
return x + y;
}
const numbers = [1, 2];
add(...numbers) // 3
  • 复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];

要记住:扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中,这里参数对象是个数组,数组里面的所有对象都是基础数据类型,将所有基础数据类型重新拷贝到新的数组中。

  • 合并数组

如果想在数组内合并数组,可以这样:

const arr1 = ['two', 'three'];
const arr2 = ['one', ...arr1, 'four', 'five'];
// ["one", "two", "three", "four", "five"]
  • 扩展运算符与解构赋值结合起来,用于生成数组
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]

需要注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...rest, last] = [1, 2, 3, 4, 5];         // 报错
const [first, ...rest, last] = [1, 2, 3, 4, 5]; // 报错
  • 将字符串转为真正的数组
[...'hello']    // [ "h", "e", "l", "l", "o" ]
  • 任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组

比较常见的应用是可以将某些数据结构转为数组:

// arguments对象
function foo() {
const args = [...arguments];
}

用于替换es5中的Array.prototype.slice.call(arguments)写法。

  • 使用Math函数获取数组中特定的值
const numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1
Math.max(...numbers); // 9

7. Proxy 可以实现什么功能?

在 Vue3.0 中通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。

Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。

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

在上述代码中,通过自定义 setget 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。

当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。

8. 对对象与数组的解构的理解

解构是 ES6 提供的一种新的提取数据的模式,这种模式能够从对象或数组里有针对性地拿到想要的数值。

1)数组的解构

在解构数组时,以元素的位置为匹配条件来提取想要的数据的:

const [a, b, c] = [1, 2, 3]

最终,a、b、c分别被赋予了数组第0、1、2个索引位的值:

数组里的0、1、2索引位的元素值,精准地被映射到了左侧的第0、1、2个变量里去,这就是数组解构的工作模式。还可以通过给左侧变量数组设置空占位的方式,实现对数组中某几个元素的精准提取:

const [a,,c] = [1,2,3]

2)对象的解构

对象解构比数组结构稍微复杂一些,也更显强大。在解构对象时,是以属性的名称为匹配条件,来提取想要的数据的。现在定义一个对象:

const stu = {
name: 'Bob',
age: 24
}

假如想要解构它的两个自有属性,可以这样:

const { name, age } = stu

这样就得到了 name 和 age 两个和 stu 平级的变量:

注意,对象解构严格以属性名作为定位依据,所以就算调换了 name 和 age 的位置,结果也是一样的:

const { age, name } = stu

9. 如何提取高度嵌套的对象里的指定属性?

有时会遇到一些嵌套程度非常深的对象:

const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
}
}
}

像此处的 name 这个变量,嵌套了四层,此时如果仍然尝试老方法来提取它:

const { name } = school

显然是不奏效的,因为 school 这个对象本身是没有 name 这个属性的,name 位于 school 对象的“儿子的儿子”对象里面。要想把 name 提取出来,一种比较笨的方法是逐层解构:

const { classes } = school
const { stu } = classes
const { name } = stu
name // 'Bob'

但是还有一种更标准的做法,可以用一行代码来解决这个问题:

const { classes: { stu: { name } }} = school

console.log(name)  // 'Bob'

可以在解构出来的变量名右侧,通过冒号+{目标属性名}这种形式,进一步解构它,一直解构到拿到目标数据为止。

10. 对 rest 参数的理解

扩展运算符被用在函数形参上时,它还可以把一个分离的参数序列整合成一个数组

function mutiple(...args) {
let result = 1;
for (var val of args) {
result *= val;
}
return result;
}
mutiple(1, 2, 3, 4) // 24

这里,传入 mutiple 的是四个分离的参数,但是如果在 mutiple 函数里尝试输出 args 的值,会发现它是一个数组:

function mutiple(...args) {
console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]

这就是 … rest运算符的又一层威力了,它可以把函数的多个入参收敛进一个数组里。这一点经常用于获取函数的多余参数,或者像上面这样处理函数参数个数不确定的情况。

11. ES6中模板语法与字符串处理

ES6 提出了“模板语法”的概念。在 ES6 以前,拼接字符串是很麻烦的事情:

var name = 'css'
var career = 'coder'
var hobby = ['coding', 'writing']
var finalString = 'my name is ' + name + ', I work as a ' + career + ', I love ' + hobby[0] + ' and ' + hobby[1]

仅仅几个变量,写了这么多加号,还要时刻小心里面的空格和标点符号有没有跟错地方。但是有了模板字符串,拼接难度直线下降:

var name = 'css'
var career = 'coder'
var hobby = ['coding', 'writing']
var finalString = `my name is ${name}, I work as a ${career} I love ${hobby[0]} and ${hobby[1]}`

字符串不仅更容易拼了,也更易读了,代码整体的质量都变高了。这就是模板字符串的第一个优势——允许用${}的方式嵌入变量。但这还不是问题的关键,模板字符串的关键优势有两个:

  • 在模板字符串中,空格、缩进、换行都会被保留
  • 模板字符串完全支持“运算”式的表达式,可以在${}里完成一些计算

基于第一点,可以在模板字符串里无障碍地直接写 html 代码:

let list = `
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
`;
console.log(message); // 正确输出,不存在报错

基于第二点,可以把一些简单的计算和调用丢进 ${} 来做:

function add(a, b) {
const finalString = `${a} + ${b} = ${a+b}`
console.log(finalString)
}
add(1, 2) // 输出 '1 + 2 = 3'

除了模板语法外, ES6中还新增了一系列的字符串方法用于提升开发效率:

  • 存在性判定:在过去,当判断一个字符/字符串是否在某字符串中时,只能用 indexOf > -1 来做。现在 ES6 提供了三个方法:includes、startsWith、endsWith,它们都会返回一个布尔值来告诉你是否存在。
    • includes:判断字符串与子串的包含关系:
const son = 'haha'
const father = 'xixi haha hehe'
father.includes(son) // true
    • startsWith:判断字符串是否以某个/某串字符开头:
const father = 'xixi haha hehe'
father.startsWith('haha') // false
father.startsWith('xixi') // true
    • endsWith:判断字符串是否以某个/某串字符结尾:
const father = 'xixi haha hehe'
father.endsWith('hehe') // true
  • 自动重复:可以使用 repeat 方法来使同一个字符串输出多次(被连续复制多次):
const sourceCode = 'repeat for 3 times;'
const repeated = sourceCode.repeat(3)
console.log(repeated) // repeat for 3 times;repeat for 3 times;repeat for 3 times;

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

JavaScript知识总结 ES6篇的更多相关文章

  1. WEBBASE篇: 第十一篇, JavaScript知识6

    JavaScript 知识6 一, String 对象 1,分隔字符串, 函数: split(seperator) 作用: 将字符串,通过seperator 拆分成一个数组: eg: var msg= ...

  2. WEBBASE篇: 第十篇, JavaScript知识5

    JavaScript知识5 <!doctype html> <html lang="en"> <head> <meta charset=& ...

  3. jQuery学习笔记 - 基础知识扫盲入门篇

    jQuery学习笔记 - 基础知识扫盲入门篇 2013-06-16 18:42 by 全新时代, 11 阅读, 0 评论, 收藏, 编辑 1.为什么要使用jQuery? 提供了强大的功能函数解决浏览器 ...

  4. JavaScript 现状:方言篇

    导读 JavaScript 和其他编程语言有一个很大的不同,它不像单纯的一个语言,而像一个由众多方言组成大家族.从 2009 年 CoffeeScript 出现开始,近几年出现了大量基于 JavaSc ...

  5. Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识

    摘要: 详解原型污染. 原文:Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识 作者:Lucas HC Fundebug经授权转载,版权归原作者所有. 可能有信息敏感的同学已 ...

  6. javaScript知识储备

    javaScript知识储备 组成 ECMAScript(核心) 提供语法.变量等,遵循ECMA-262标准 DOM(文档对象模型) 提供操作HTML标签的API,遵循W3C规范 BOM(浏览器对象模 ...

  7. JavaScript面向对象—深入ES6的class

    JavaScript面向对象-深入ES6的class 前言 在前面一篇中主要介绍了JavaScript中使用构造函数+原型链实现继承,从实现的步骤来说还是比较繁琐的.在ES6中推出的class的关键字 ...

  8. JavaScript 面向对象(三) —— 高级篇

    JavaScript 面向对象(一) —— 基础篇 JavaScript 面向对象(二) —— 案例篇 一.json方式的面向对象 首先要知道,js中出现的东西都能够放到json中.关于json数据格 ...

  9. JavaScript 面向对象(二) —— 案例篇

    看案例前可以先看看基础篇:JavaScript 面向对象(一) —— 基础篇 案例——面向对象的选项卡:把面向过程的程序一步步改成面向对象的形式,使其能够更加的通用(但是通用的东西,一般会比较臃肿). ...

  10. JavaScript 面向对象(一) —— 基础篇

    学好JS的面向对象,能很大程度上提高代码的重用率,像jQuery,easyui等,这篇博客主要从细节上一步步讲JS中如何有效地创建对象,也可以看到常见的创建对象的方式,最后也会附上一些JS面向对象的案 ...

随机推荐

  1. CF1010C Border 题解

    题目传送门 前置知识 最大公约数 | 裴蜀定理 简化题意 给定一个长度为 \(n\) 的序列 \(a\),求 \((\sum\limits_{i=1}^{n}d_ia_i) \bmod k\) 一共会 ...

  2. NC51178 没有上司的舞会

    题目链接 题目 题目描述 Ural大学有N名职员,编号为1~N. 他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司. 每个职员有一个快乐指数,用整数 \(Hi\) 给出,其中 \(1\le ...

  3. 【OpenGL ES】FBO离屏渲染

    1 前言 ​ OpenGL 默认把 framebuffer 当作渲染目的地,它由窗口系统创建并管理.应用程序也可以创建额外非可显示的 framebuffer object(FBO),以区别窗口系统提供 ...

  4. Annotation-specified bean name conflicts with existing

    问题说明 Annotation-specified bean name conflicts with existing,non-compatible bean definition of same n ...

  5. 敏感信息泄露之如何隐藏IIS服务器名称和版本号

    1.问题说明 请求IIS部署的网站可以发现响应头中暴露了IIS服务器名称/版本号. 漏洞等级:中 2.解决方案 想办法隐藏掉这部分信息. 2.1 下载并安装微软官方IIS扩展插件 URL Rewrit ...

  6. java zTree异步加载实战

    zTree简介 zTree 是一个依靠 jQuery 实现的多功能 "树插件".优异的性能.灵活的配置.多种功能的组合是 zTree 最大优点. zTree 是开源免费的软件(MI ...

  7. CSS实现导航栏

    1.知识点 列表 浮动 伪类 背景 文本格式化 盒子模型 2.效果 3.代码 <!DOCTYPE html> <html lang="en"> <he ...

  8. SpringCloud SpringBoot 组件使用:使用Nacos作为服务的注册中心和配置中心

    基础篇 一.什么是Nacos? 官方介绍是这样的: Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您实现动态服务发现.服务配置管理.服务及流量管理. Na ...

  9. C++ 多线程的错误和如何避免(4)

    对共享的资源或者数据做加锁处理 在多线程的环境下,有时需要多个线程对同一个资源或者数据进行操作,如果没有加锁,容易出现未定义的行为. 比如: #include <iostream> #in ...

  10. C++ 多线程的错误和如何避免(2)

    试图 join 一个已经 detach 的线程 如果你已经在某个地方分离了线程,那你不可以在主线程再次 join,这是一个明显的错误 比如: #include <iostream> #in ...