一,箭头函数和普通函数的区别
箭头函数简介:箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式第一种,省略{}和rerurn。第二种可以包含多条语句,这时候就不能省略{ ... }和return,如果参数不是一个,就需要用括号()括起来:
//第一种
x => x * x
//第二种
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
等同于
//第一种
function (x){
return x*x
}
//第二种
function (x){
if(x)>0{
return x*x
}
else{
return -x*x
}
}
区别:
1、首先箭头函数作为匿名函数,是不能作为构造函数的,不能使用new
2、this,普通函数构造函数的 this 指向了一个新的对象;如果函数是作为对象的方法被调用的,则其 this 指向了那个调用它的对象;但是箭头函数的则会捕获其所在上下文的 this 值,作为自己的 this 值。在使用call或apply绑定时,相当于只是传入了参数,对this没有影响
3、箭头函数不绑定arguments,取而代之用rest参数…解决
4、箭头函数当方法使用的时候没有定义this绑定
var obj = {
i: 10,
a: () => console.log(this.i, this),
b: function() {
console.log( this.i, this)
}
}
obj.a(); //undefined, Window
obj.b(); // 10, Object {...}
5、箭头函数不能当做Generator函数,不能使用yield关键字
6、不能简单返回对象字面量
7、箭头函数不能换行
二.用户上传图片,图片存储的地址
图片保存在服务器上,数据库存储的是图片的链接地址,数据库是不会真的把一个文件存进去的 他只是把这个文件存储的地址保存起来
三.let,var 的区别
1.var的作用于是函数作用于,而let是块级别(大括号括起来的内容),for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
2.不存在变量提升,let所声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(lemon); // undefined
var lemon= 1;
// let 的情况
console.log(zoey); // 报错
let zoey= 1;
3.暂时性死区
es6中规定,区域块中存在let和const命令,这个区块对这些命令声明的变量,最开始就是一个封闭的作用域,在申明之前就使用这些变量就报错,在let命令声明变量之前使用该变量都是不可以的。
var a = 1;
if(2>1){
a = 3,//报错
let a
}
还有一些暂时性死区隐藏得比较深比如下面代码
//第一种
function sum(x=y,y=1){
return [x,y]
}
sum()//报错,因为x=y时,y还没有赋值
//但是这样就没毛病
function sum(x=1,y=x){
return [x,y]
}
sum()//[1,1]
//第二种
var x = x//不报错
console.log(x);//undefined
let x = x;//报错:x is not defined
4.不允许重复声明
// 报错
function fn() {
let a = 1;
let a = 2;//Identifier 'a' has already been declared
}
fn()
// 报错
function fn1() {
let a = 1;
var a = 2;//Identifier 'a' has already been declared
}
fn1()
function fn2(arg) {
let arg; // 报错
}
function fn3(arg) {
{
let arg; // 不报错
}
}
5.let实际上为 JavaScript 新增了块级作用域
- ES6 允许块级作用域的任意嵌套,内层作用域可以定义外层作用域的同名变量。
- ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。
- ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
四.利用generator完成fibnaqi数列
1.什么是generator
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。
在 ES6 中定义一个生成器函数很简单,在 function 后跟上「*」即可:
function* foo1() { };
function *foo2() { };
function * foo3() { };
foo1.toString(); // "function* foo1() { }"
foo2.toString(); // "function* foo2() { }"
foo3.toString(); // "function* foo3() { }"
foo1.constructor; // function GeneratorFunction() { [native code] }
调用生成器函数会产生一个生成器(generator)。生成器拥有的最重要的方法是 next(),用来迭代:
function* foo() { };
var bar = foo();
bar.next(); // Object {value: undefined, done: true}
上面第 2 行的语句看上去是函数调用,但这时候函数代码并没有执行;一直要等到第 3 行调用 next 方法才会执行。next 方法返回一个拥有 value 和 done 两个字段的对象。
生成器函数通常和 yield 关键字同时使用。函数执行到每个 yield 时都会中断并返回 yield 的右值(通过 next 方法返回对象中的 value 字段)。下次调用 next,函数会从 yield 的下一个语句继续执行。等到整个函数执行完,next 方法返回的 done 字段会变成 true。下面看一个简单的例子:
function* list() {
for(var i = 0; i < arguments.length; i++) {
yield arguments[i];
}
return "done.";
}
var o = list(1, 2, 3);
o.next(); // Object {value: 1, done: false}
o.next(); // Object {value: 2, done: false}
o.next(); // Object {value: 3, done: false}
o.next(); // Object {value: "done.", done: true}
o.next(); // Error: Generator has already finished
每次调用 next 方法,都会得到当前 yield 的值。函数执行完之后,再调用 next 方法会产生异常。
function* gen(x) {
var y = yield x + 2;
return y;
}
var g = gen(1);
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true }
上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)g。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x + 2为止。
换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。
2.用generater完成fibnaqi数列
function* fab(max) {
var count = 0, last = 0, current = 1;
while(max > count++) {
yield current;
var tmp = current;
current += last;
last = tmp;
}
}
var o = fab(10), ret, result = [];
while(!(ret = o.next()).done) {
result.push(ret.value);
}
console.log(result); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
ES6 也规定了类似的语法「for ... of」用来迭代
function fab(max) {
var count = 0, last = 0, current = 1;
while(count++ < max) {
yield current;
var tmp = current;
current += last;
last = tmp;
}
}
for(var i of fab(10)) {
console.log(i);
}
五.ES6 中class
1.定义
ES6提出了类(Class)的概念,让对象的原型的写法更像面向对象语言写法。 ES6中通过class定义对象,默认具有constructor方法和自定义方法。ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已.
- 类的数据类型就是函数,类本身就指向构造函数。使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。
- 类和模块的内部,默认就是严格模式
2.基本语法
function Point (x,y){
this.x=x;
this.y=y;
}
Point.prototype.toString= function(){
return this.x+this.y;
}
注意:constructor方法对应ES5的构造函数;创建类的方法不需要使用function关键字,方法之间不需要逗号分隔。
ES5中定义对象时组合使用构造函数模式和原型模式,构造函数模式用于定义实例属性,原型模式用于定义共享方法和共享属性,节省内存,而且支持向构造函数传递参数。
function Point (x,y){
this.x=x;
this.y=y;
}
Point.prototype.toString= function(){
return this.x+this.y;
}
构造函数的原型(prototype)属性在ES6中依旧存在,这一点和ES5一样:类的方法都定义在类的原型(prototype)上,在类的实例上面调用方法,其实就是调用原型上的方法。
//实例的constructor方法就是类的原型(prototype)的constructor方法
class B{
constructor(){}
}
let b = new B();
console.log(b.constructor === B.prototype.constructor);//true
3.ES6 Class的继承
Class通过extends关键字实现继承
class ColorPoint extends Point{}
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // super代表父类原型,调用父类的toString()
}
}
上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
super这个关键字,既可以当作函数使用,也可以当作对象使用。第一种情况,super作为函数调用时,代表父类的构造函数,只能用在子类的构造函数中。ES6 要求,子类的构造函数必须执行一次super函数。第二种情况,super作为对象时,指代父类的原型对象。
如果子类没有定义constructor方法,这个方法会被默认添加。在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}