ES6之前JavaScript变量的作用域,是以函数来作为界定的,在函数内部定义的变量,正常情况下在外部是无法使用的,没有块级作用域这一概念,并且,变量可以重复声明。如果你没有接触过其他编程语言,你会觉得这还行,重复声明下面的覆盖上面的。但是你找一Java哥来,他肯定说这怎么能行。
ES6的出现也是为了解决JavaScript中不完善的地方
下面正式开始
let与const
let 与var的区别,
1.let 与const,有块级作用域概念,出了当前块级作用域,变量将不可被访问。var 有变量提升这一概念,let与const则没有。
如下,var有变量提升 let 没有,const也是如此
块级作用域,很多人不理解,但是直白点说,块级作用域就是在 ‘{}’
左右花括号之间的代码,
我们常见的有
function (){}
for (){}
if(){},
// 其实仅仅有两个花括号{}也事一个语法快
{}
下面举个例子
{
var a = 1;
let b = 2;
const C = 3;
}
console.log(a) // 1
console.log(b) // 报错
console.log(C) // 报错
const从字面意思来说是常量,在JavaScript中也是来定义常量的(与PHP一样);在工作中定义常量一般用大写,用于区分,这只是书写习惯问题,
这里可能有人会问,这两个有什么用,下面我来举个简单的例子说明一下
html部分很简单,就只有四个按钮
<input type="button" value="000">
<input type="button" value="111">
<input type="button" value="222">
<input type="button" value="333">
需求是这样的,每点一次按钮打印相应按钮的索引
var input = document.getElementsByTagName('input');
for (var i = 0; i < input.length; i++) {
input[i].onclick = function () {
alert(i)
}
}
我们这样写就会发现一个很严重的问题,这样点击任何一个按钮弹出的都是4,
于是就有了这样一个写法,使用闭包的方式解决问题。
for (var i = 0; i < input.length; i++) {
(function (i) {
input[i].onclick = function () {
alert(i)
}
})(i)
}
但是使用ES6的方式只需要将var改成let即可
for (let i = 0; i < input.length; i++) {
input[i].onclick = function () {
alert(i)
}
}
这样是不是简单很多?
在这里提到闭包这个概念,在很多人面试的时候也被问道这个问题,简述闭包的概念。
在这个例子中很多人也会用到过闭包这样的方式去解决这样的问题,但是对其真正原理不是非常的清晰,在这简单阐述下闭包(如有不对请指正)
闭包:表现形式是函数嵌套,一个函数可以访问另一个函数的局部变量。而闭包的真正意义是,将函数里面的局部变量保存下来,方便以后调用。
在上面的例子中使用 var 没有块级作用域的概念,每次循环出来之后 i 已经被加到了 4 ,所以我们看到的是 4 的原因,而通过自执行函数,函数每一次执行,都将当时的 i 给保存了下来,因为在我们点击的时候还需要当时的 i (在i是0,1,2,3的时候),而 let 本身就有块级作用域的概念,所以每次循环都是有一个单独的 i
基本就是这样一个原理,这里不做过多赘述。
let add = function (a) {
return a + 1
}
// 可以改写成这样
let add = (a) => {
return a + 1
}
console.log(add(1))
其实就是将function关键字省去用 => 代替
这里说下
1.当有且仅有一个形式参数的时候()可以省去
2.当返回值有且仅有一个值或者函数返回值有且仅有一个表达式的时候 return 和{}也可以省去
所以上面的例子可以写成这样
var add = a => a + 1
其实也很容易理解:add函数,就是传一个a进去,输出a+1,学过Python的老哥一眼就能看出这是借鉴的Python。
箭头函数:出现的作用除了让函数的书写变得很简洁,可读性很好外;最大的优点是解决了this执行环境所造成的一些问题。
首先看一个简单的例子
在普通函数中
let name = 'Bar';
let obj = {
name: 'Foo',
show: function(){
console.log(this.name) // 打印为Foo
}
}
obj.show()
一般的定义函数跟我们的理解是一样的,运行的时候决定this的指向,我们可以知道当运行obj.show()时候,this指向的是obj这个对象
在箭头函数中
let name = 'Bar';
let obj = {
name: 'Foo',
show: ()=>{
console.log(this.name) // 打印结果Bar
}
}
obj.show()
箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定
这里做下说明:所谓的定义时候绑定,就是this是继承自父执行上下文!!,比如这里的箭头函数中的this.name,箭头函数本身与show平级以key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.x=name实际上表示的是window.name,因此输出的是Bar。(this只有在函数被调用,或者通过构造函数new Object()的形式才会有this)
1.箭头函数的this绑定看的是this所在的函数定义在哪个对象下,绑定到哪个对象则this就指向哪个对象
2.如果有对象嵌套的情况,则this绑定到最近的一层对象上
JSON.stringify()
JSON.parse()
var obj = {
name: 'Foo',
age: 18
}
let str1 = JSON.stringify(obj)
console.log(str1)
var str2 = '{"name":"Foo","age":18}'
let obj1 = JSON.parse(str2)
console.log(obj1)
键值一样可以只写一个
let age = 18;
obja = {
age
}
console.log(obja.age) // 18
var strA = 'http://wwww.mi.com';
var isHttp = strA.startsWith('https://')
var isCom = strA.endsWith('com')
console.log(isHttp) // false
console.log(isCom) // true
var str = "Hello world, welcome to the Runoob.";
var n = str.startsWith("world", 6);
console.log(n) // true
不完全属于ES6简单提一下,比较简单
一个一个来
1.forEach就是一个简单的遍历,接收一个函数,这个函数有两个参数,第一个是数组中的值,第二个是当前索引
let arr = [1,2,3,4,5,6];
arr.forEach((item, index) => {
console.log(`数组第${index}元素是${item}`)
})
2.map映射
接收一个函数,两个参数,当前数组的值与对应索引,返回一个新数组,
let arr = [1,2,3,4,5,6];
let arr1 = arr.map((item, index) => {
return item + '单位'
})
console.log(arr1)
3.filter过滤
参数与前两者一样,返回一个新数组,过滤:简单来说就是符合条件的留下,不符合的踢走。
let arr = [1,2,3,4,5,6];
let arr1 = arr.filter((item, index) => {
return item >=4
})
console.log(arr1)
输出结果: [4,5,6]
4.reduce(相对复杂一点的)
英语直译的意思是减少,肯定不符合,在这里叫他迭代更加符合他的功能
语法结构arr.reduce(callback,[initialValue])
回调函数接收四个参数
prev: 上一次调用回调返回的值,或者是提供的初始值(initialValue))
next: 数组中当前被处理的元素
index:数组中当前被处理元素的索引
arr:调用 reduce 的数组
直接上例子
let arr = [1,2,3,4,5,6];
let sum = arr.reduce((prev, next, index) => {
console.log(prev,next,index)
return prev + next
})
console.log(sum)
打印结果:
这里的prev并不是真正的前一个,而是前一次执行的返回值。
可以帮我们做很多事情,比如说求和,且性能是比for循环更好。
我们先看一下ES5的面向对象是怎么写的
// 类/构造函数
function User (name, age) {
this.name = name;
this.age = age;
}
User.prototype.sayName = function () {
console.log(this.name)
}
// 继承User
function VipUser (name, age ,level) {
User.call(this, name, age) // 集成
this.level = level
}
VipUser.prototype = new User();
VipUser.prototype.constructor = VipUser // 还原构造函数
VipUser.prototype.showLevel = function () {
console.log(this.level)
}
var v1 = new VipUser('Foo', 18,3)
v1.sayName()
v1.showLevel()
我们可以看出ES6之前的面向对象写法,是基于函数与原型链,类和构造函数其实是一个东西。
我们现在来看下ES6的写法,新增class关键字
class User {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayName () {
console.log(this.name)
}
}
// 继承User
class VipUser extends User{
constructor(name, age, level) { // 构造函数
super(name, age) //继承父类(超类)
this.level = level
}
showLevel () {
console.log(this.level)
}
}
var v1 = new VipUser('Foo', 18,3)
v1.sayName()
v1.showLevel()
ES6在面向对象的写法上有了很大的优化,将类与构造函数的概念区分开来,继承使用extends+super就可以完成对于父类的继承。
待更新…