ES6经常使用知识学习札记

转载请注明出处javascript

原文链接 http://blog.huanghanlian.com/article/5c7aa6c7bf3acc0864870f9dhtml

es6 是什么

首先弄明白ECMA和js的关系。ECMA是标准,Javascript是ECMA的实现。由于js也是一种语言,但凡语言都有一套标准,而ECMA就是javascript的标准。 在2015年正式发布了ECMAscript6.0,简称ES6,又称为ECMAscript2015。前端

  1. 历史
    1. ECMAScript和Javascript
      • ECMA是标准,JS是实现
        • 相似于HTML5是标准,IE10,Chrome,FF都是实现
        • 换句话说,未来也能有其余XXXScript来实现ECMA
      • ECMAScript简称ECMA或ES
      • 目前版本
        • 低级浏览器主要支持ES 3.1
        • 高级浏览器正在从ES 5过分ES 6
  2. 历史版本
时间 ECMA JS 解释
1996.11 EC 1.0 JS稳定 Netscript将js提交给ECMA组织,ES正式出现
1998.06 ES 2.0 ES2正式发布
1999.12 ES 3.0 ES3被普遍接受
2007.10 ES 4.0 ES4过于激进,被废了
2008.07 ES 3.1 4.0退化为严重缩水版的3.1
由于吵得太厉害,因此ES3.1代号为Harmony(和谐)
2009.12 ES 5.0 ES5正式发布
同时公布了JavaScript.next也就是后来的ES6.0
2011.06 ES 5.1 ES5.1成为了ISO国际标准
2013.03 ES 6.0 ES6.0草案定稿
2013.12 ES 6.0 ES6.0草案发布
2015.06 ES 6.0 ES6.0预计发布正式版
JavaScript.next开始指向ES 7.0

ES6兼容性和新特性

es5兼容性java

http://kangax.github.io/compat-table/es5/node

继小鹏

es6兼容性react

http://kangax.github.io/compat-table/es6/git

继小鹏

ES6(ES2015)-- IE10+,Chrome,FireFox,移动端,NodeJS。这些环境基本上都是认得,都能兼容es6

可是有需求兼容ie怎么办github

有两种办法ajax

比方说在移动端或者是混合开发当中,多去用用ES6,在老的版本中不用。惹不起咋躲得起。

编译,转换

  1. 在线转换
    • 简单来讲就是写好了ES6了而后引用一个js库进来。我什么也不用作了。它替我去作了各类各样的事情
    • 缺点,用户每次打开页面都要从新转换一遍,性能体验不是很好。
  2. 提早编译

ES6的到底有什么样的东西?

  1. 变量(对原有的变量作了修改)
  2. 函数(对原有的函数也作了修改)
  3. 数组(对数组作了一些改进)
  4. 字符串(改进)
  5. 面向对象
  6. Promise(串行化的异步请求方式)
  7. yield && generator(generator是专门把同步操做拆成异步操做,generator是对Promise的一个封装)
  8. 模块化

变量-let和const

回顾ES5是怎么生明变量的,有什么样的缺点

var的缺点

  1. 能够重复声明
  2. 没法限制修改
  3. 没有块级做用域

能够重复声明

最大的问题

var a=12;
var a=5;
alert(a);//弹窗5

会发现5能出来,没有报错,没有警告,什么都没有

这在其余语言是不可出现的。

没法限制修改

在程序中,有些东西是永远不变的。

比方说常量
PI=3.1415926
是不会发生改变
在不少语言中都有常量的概念。在js中没有

至少var不是一个常量。

换句话说,要不要改,能不能让别人别动这个值,不要改这个值。全凭自觉。

为何java是全世界最流行的一门语言

缘由很简单,由于他很是的严谨,他很是的死板。

相信一件事,越是容易的语言,越是简单的语言。其实是不严谨。就无法去开发大型项目

反过来他可能让你以为很难受的语言java,对你限制很严格。可是你掌握了呀以后,开发起大型应用会很是的驾轻就熟。

没有块级做用域

es5 只在函数中支持块级做用域

{
    //这就是语法块
}
if(){
    变量=xxxx
}
//变量出来就用不了了,这就是块做用域

for(){

}

体现块级做用域做用

if(true){
    var a=12;
}
alert(a);
//在块级做用域内声明变量。在外部依然可以访问

在ES6中有了两种新的定义变量的方式

letconst

let

  • 不能重复声明,let是变量,能够修改,块级做用域
  • 块级做用域
  • 可修改let变量的值

const

  • 不可重复声明,const常量,不能修改,块级做用域
  • 块级做用域
  • 不可修改const变量的值

let 不能重复声明例子

let a=12;
let a=5;
console.log(a);
//报错
//Uncaught SyntaxError: Identifier 'a' has already been declared
//不能重复声明

const 不能重复声明例子

const a=12;
const a=5;
console.log(a);
//报错
//Uncaught SyntaxError: Identifier 'a' has already been declared
//不能重复声明

在大型项目中,重复声明这件事,指不定你定义了什么东西别人也定义了。还不报错,到时候定位bug很难找。

变量和常量

变量

let a=12;//声明变量赋值
a=5;//给变量a赋值
console.log(a);//你会明确的发现它变成了5

常量

const a=12;
a=5;
console.log(a);

报错,不能对常量赋值
继小鹏

块级做用域

var 块级做用域只在函数中体现,也就是说在函数中var声明的变量不会在全局做用域中体现

function aa(){
    var a=1;
    console.log(a)
}
aa();
console.log(a)

继小鹏

let和const只在块级做用域,或者在语法块以内起做用

if(true){
    let a=12;
}
console.log(a);//Uncaught ReferenceError: a is not defined
if(true){
    const a=12;
}
console.log(a);//Uncaught ReferenceError: a is not defined

语言推出一个新的版本,一个更好的版本,他必定是要解决一些原来有的问题,ES6也不例外。

块级做用域有什么

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <input type="button" value="按钮1">
    <input type="button" value="按钮2">
    <input type="button" value="按钮3">
</body>
<script type="text/javascript">
    window.onload=function(){
        var aBtn=document.getElementsByTagName('input');
        for (var i = 0; i < aBtn.length; i++) {
            aBtn[i].onclick=function(){
                alert(i)
            };
        }
    };
</script>
</html>

继小鹏

以上代码执行,无论按哪一个按钮弹出都是3

因为var声明变量只在函数做用域中扩散到全局

在for或者if快级做用域中声明的变量会在局部或全局生效

for循环执行完毕,i这个变量暴露到全局了,等于3

因此for循环中执行的事件绑定,是将点击事件回调函数执行。当点击按钮时候,会出发绑定回调函数,此时当前做用域中,i等于3,因此不管点击哪一个按钮弹出都是3

之前咱们是经过闭包解决这个问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <input type="button" value="按钮1">
    <input type="button" value="按钮2">
    <input type="button" value="按钮3">
</body>
<script type="text/javascript">
    window.onload=function(){
        var aBtn=document.getElementsByTagName('input');
        for (var i = 0; i < aBtn.length; i++) {
            ! function(i) {
                aBtn[i].onclick=function(){
                    alert(i)
                };
            }(i);
        }
        console.log(i)
    };
</script>
</html>

在每一层循环的时候,用一个匿名函数并且是当即执行的匿名函数给他包装起来,而后将每一次遍历的1.2.3分别的值去传到这个匿名函数里,而后匿名函数接到这个参数i再放到点击事件中去引用i当咱们每次点击事件输出的值i就会取每个闭包环境下的i。因此这样就能达到效果。

使用let来实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <input type="button" value="按钮1">
    <input type="button" value="按钮2">
    <input type="button" value="按钮3">
</body>
<script type="text/javascript">
    window.onload=function(){
        var aBtn=document.getElementsByTagName('input');
        for (let i = 0; i < aBtn.length; i++) {
            aBtn[i].onclick=function(){
                alert(i)
            };
        }
        console.log(i)
    };
</script>
</html>

继小鹏

for循环自己就是一个语法块,自身就是一个块

因为var只把函数做为做用域

因此以上须要经过当即执行函数来包一层,来实现效果。

let自己是支持块级做用域的,因此电脑按钮执行回掉函数,打印i,是当前块级做用域下的i

这个i在非for块做用域下是未定义的。

函数-箭头函数

箭头函数在写法上对es5作了一些修整,代码看起来更显得简洁

  • 若是只有一个参数,圆括号"()"能够省略
  • 函数体若是只有一句return语句,花括号也能够省略
// 定义一个箭头函数
let a = (arg)=>{ //  这里=>符号就至关于function关键字
    return arg+=1
}
// 也能够简写为
let a = arg => arg+=1

箭头函数的做用跟之前接触的函数没什么本质的区别,更多的是一种写法上的变化。

function show() {

}
同等于
let show =()=>{

}
function () {

}
同等于
()=>{

}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

</body>
<script type="text/javascript">
    window.onload=function(){
        alert('123')
    };
</script>
</html>

同等于

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

</body>
<script type="text/javascript">
    window.onload=()=>{
        alert('123')
    };
</script>
</html>

箭头函数也对this的指向作了修整 es6以前的函数的this指向调用函数时所在的对象,而箭头函数的this指向函数定义时所在的对象

//普通函数
var obj = {
  say: function () {
    setTimeout(function() {
      console.log(this)
    });
  }
}
obj.say();//Window object
// 箭头函数
var obj = {
    say: function () {
        setTimeout(() => {
            console.log(this)
        });
    },
    test:123
}
obj.say(); // obj

函数-参数

  1. 参数扩展/数组展开
  2. 默认参数

参数扩展

  1. 收集剩余参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不须要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function show (a,b,...args){
    console.log(a);//1
    console.log(b);//2
    console.log(args);//[3,4,5,6]
}
show(1,2,3,4,5,6);

下面是一个 rest 参数代替arguments变量的例子。

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
  1. 展开数组

展开后的效果,跟直接把数组内容写在这同样

let arr=[1,2,3];
console.log(1,2,3);
console.log(...arr);
//1,2,3同等于...arr

继小鹏

function show(a,b,c){
    console.log(a)
    console.log(b)
    console.log(c)
}
let arr=[1,2,3];
show(1,2,3);
show(...arr);
//1,2,3同等于...arr
let arr1=[1,2,3];
let arr2=[4,5,6];

let arr=[...arr1,...arr2];
let arrS=[1,2,3,4,5,6];
//...arr1,写法,至关于将数组内容掏出来内容

默认参数

function show(a,b=5,c=6){
    //我但愿b,默认是5 不传的时候
    //我但愿c,默认是6 不传的时候
    console.log(a,b,c);//1,2,6
}
show(1,2);

解构赋值

容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。好比:

var [a,b] = [1,2]
// a=1  b=2
  1. 左右两边结构必须同样
  2. 右边必须是个合法的数据
  3. 声明和赋值必须一句话完成,不能把声明与赋值分开
let [a, b] = [1, 2]                // 左右都是数组,能够解构赋值
let {a, b} = {a:1, b:2}            // 左右都是对象,能够解构赋值
let [obj, arr] = [{a:1}, [1, 2]]   // 左右都是对象,能够解构赋值

let [a, b] = {a:1, b:2}            // err 左右结构不同,不能够解构赋值
let {a,b} = {1, 2}                 // err 右边不是一个合法的数据,不能解构赋值

let [a, b];
[a, b] = [1, 2]                    // err 声明与赋值分开,不能解构赋值

数组

数组扩展了4个方法:map、reduce、filter、forEach

  1. map 映射

经过指定函数处理数组的每一个元素,并返回处理后的数组。
一个对一个

[12,58,99,86,45,91]
[不及格,不及格,及格,及格,不及格,及格]
//将数组映射成另外一个数组
[45,57,135,28]
//将用户id映射成对象
[
    {name:'huang',role:1},
    {name:'huang1',role:2},
    {name:'huang2',role:3},
    {name:'huang4',role:1}
]

map例子

let arr=[12,5,8];
//我想要将数组内容乘与2的结果
let result=arr.map(function(item){
    console.log(item);
    //须要将你要的内容返回出来
    return item*2;
});
console.log(arr);//[12, 5, 8]
console.log(result);//[24, 10, 16]

简写1

let arr=[12,5,8];
//我想要将数组内容乘与2的结果
let result=arr.map(item=>{
    console.log(item);
    //须要将你要的内容返回出来
    return item*2;
});
console.log(arr);//[12, 5, 8]
console.log(result);//[24, 10, 16]

简写2

let arr=[12,5,8];
//我想要将数组内容乘与2的结果
let result=arr.map(item=>item*2);

console.log(arr);//[12, 5, 8]
console.log(result);//[24, 10, 16]
let arr=[12,58,99,86,45,91]
let result=arr.map(item=>item>=60?'及格':'不及格');

console.log(arr);//[12, 58, 99, 86, 45, 91]
console.log(result);//["不及格", "不及格", "及格", "及格", "不及格", "及格"]
  1. reduce 汇总

将数组元素计算为一个值(从左到右)。
一堆出一个

算个总数

let arr=[12,58,99,86,45,91]
/**
 * [description]
 * @param  {[type]} (total,currentValue,index,arr [
 * 初始值, 或者计算结束后的返回值。
 * 当前元素
 * 当前元素的索引
 * 当前元素所属的数组对象。
 * ]
 * @return {[type]}                               [返回计算结果]
 */
let result=arr.reduce((total,currentValue,index,arr)=>{
    return total+currentValue;
});
console.log(result)//391

算个平均数

let arr=[12,58,99,86,45,91]
/**
 * [description]
 * @param  {[type]} (total,currentValue,index,arr [
 * 初始值, 或者计算结束后的返回值。
 * 当前元素
 * 当前元素的索引
 * 当前元素所属的数组对象。
 * ]
 * @return {[type]}                               [返回计算结果]
 */
let result=arr.reduce((total,currentValue,index,arr)=>{
    if(index!=arr.length-1){                        //若是不是最后一次
        return total+currentValue;                  //求和
    }else{                                          //最后一次
        return (total+currentValue)/arr.length;     //求和再除于长度个数
    }
});
console.log(result)//65.16666666666667
  1. filter 过滤器

检测数值元素,并返回符合条件全部元素的数组。

定义和用法

filter() 方法建立一个新的数组,新数组中的元素是经过检查指定数组中符合条件的全部元素。

注意: filter() 不会对空数组进行检测。

注意: filter() 不会改变原始数组。

需求,能被3整除的留下,不能的去除

let arr=[12,58,99,86,45,91]

//需求,能被3整除的留下,不能的去除

/**
 * [description]
 * @param  {[type]} (currentValue,index,arr [
 * 当前元素的值
 * 当前元素的索引值
 * 当前元素属于的数组对象
 * ]
 * @return {[type]}                         [返回数组,包含了符合条件的全部元素。若是没有符合条件的元素则返回空数组。]
 */
let result=arr.filter((currentValue,index,arr)=>{
    if(currentValue%3==0){
        return true;
    }else{
        return false;
    }
});
console.log(result)//[12, 99, 45]

简写

let arr=[12,58,99,86,45,91]

//需求,能被3整除的留下,不能的去除

/**
 * [description]
 * @param  {[type]} (currentValue,index,arr [
 * 当前元素的值
 * 当前元素的索引值
 * 当前元素属于的数组对象
 * ]
 * @return {[type]}                         [返回数组,包含了符合条件的全部元素。若是没有符合条件的元素则返回空数组。]
 */
let result=arr.filter((currentValue,index,arr)=>{
    return currentValue%3==0;
});
console.log(result)//[12, 99, 45]

再次简写

let arr=[12,58,99,86,45,91]
let result=arr.filter(currentValue=>currentValue%3==0);
console.log(result)//[12, 99, 45]
  1. forEach 循环(迭代)

数组每一个元素都执行一次回调函数。

forEach() 方法用于调用数组的每一个元素,并将元素传递给回调函数。

注意: forEach() 对于空数组是不会执行回调函数的。

字符串

  1. 多了两个新方法
  2. 字符串模板

多了两个新方法

  • startsWith() 表示参数字符串是否在原字符串的头部,返回布尔值

startsWith应用

let str='http://blog.huanghanlian.com'
if(str.startsWith('http://')){
    console.log('普通网址');
}else if(str.startsWith('https://')){
    console.log('加密网址');
}else if(str.startsWith('git://')){
    console.log('git网址');
}else if(str.startsWith('svn://')){
    console.log('svn网址');
}else{
    console.log('其余')
}
  • endsWith() 表示参数字符串是否在原字符串的尾部,返回布尔值
let str='http://blog.huanghanlian.com/sitemap.xml'
if(str.endsWith('.xml')){
    console.log('网站地图');
}else if(str.endsWith('.jpg')){
    console.log('图片');
}else if(str.endsWith('.txt')){
    console.log('文本文件');
}else{
    console.log('其余')
}

//网站地图
  • includes() 表示是否在原字符串找到了参数字符串,返回布尔值

字符串模板

模板字符串有两个能力

  1. 能直接把变量塞到字符串中去。
  2. 能够折行

日常写字符串有两种写法,

let str="abc";
let str2='efg';

一种是单引号,一种是双引号。在js里都能用。没什么区别

如今出来一种新的字符串

let str=`abc`;

这种符号叫反单引号

简单使用例子

let a=12;
let str=`a${a}bc`;
console.log(str);//a12bc

反单引号中的美圆符号带上花括号他的做用就是把变量直接塞进字符串里面去。

例子

之前字符串拼接

let title='我是标题';
let content='我是内容';

let str='<div class="wrap">\
            <h1>'+title+'</h1>\
            <p>'+content+'</p>\
        </div>';
document.body.innerHTML=str;
console.log(str);

有了字符串模板后的写法

let title='我是标题';
let content='我是内容';

let str='<div class="wrap">\
            <h1>'+title+'</h1>\
            <p>'+content+'</p>\
        </div>';

let str2=`<div class="wrap">
            <h1>${title}</h1>
            <p>${content}</p>
        </div>`;
document.body.innerHTML=str2;
console.log(str2);

面向对象-基础

面向对象

es5面向对象

function User(name,pass){
    this.name=name;
    this.pass=pass;
}

//给这个类加原型方法
/**
 * [showName 获取用户名]
 * @return {[type]} [返回用户名]
 */
User.prototype.showName=function(){
    console.log(this.name);
    return this.name;
};

/**
 * [showPass 获取用户密码]
 * @return {[type]} [返回用户密码]
 */
User.prototype.showPass=function(){
    console.log(this.pass);
    return this.pass;
};


var ul=new User('黄继鹏','abc');
//调用类方法
ul.showName();//黄继鹏

这样写的缺点

  1. 类和构造函数不分,
  2. 类散开了,先声明一个构造函数,而后对函数原型添加方法

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,做为对象的模板。经过class关键字,能够定义类。 先看如何定义一个class类:

class User {
    constructor(name) {          // 构造器,至关于es5中的构造函数
        this.name = name         // 实例属性
    }
    showName(){                  // 定义类的方法,不能使用function关键字,不能使用逗号分隔
        console.log(this.name)
    }
}
var foo = new User('黄继鹏')
foo.showName();//黄继鹏

(1)constructor

  • es6中class类专用的构造器,至关于以前定义的构造函数,每一个类都必须有constructor,若是没有则自动添加一个空的constructor构造器。
  • 建立实例的时候自动执行constructor函数
  • constructor中的this指向实例,而且默认返回this(实例)

(2)class类的prototype

其实class的基本类型就是函数(typeof User = "function"),既然是函数,那么就会有prototype属性。

类的全部方法都是定义在prototype上

class User {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}
User.toValue()             // err User.toValue is not a function
User.prototype.toValue()   // 能够调用toValue方法

// 等同于

User.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

(3)类的实例

  • 类的实例只能经过new来建立
  • 除了静态方法,定义在类上的全部的方法都会被实例继承
  • 除非定义在类的this对象上才是实例属性,不然都是定义在类的原型(prototype)上
//定义类
class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.proto.hasOwnProperty('toString') // true

(4)静态方法

若是在类中定义一个方法的前面加上static关键字,就表示定义一个静态方法,静态方法不会被实例继承,但会被子类继承,因此不能经过实例使用静态方法,而是经过类直接调用

class User {
    constructor(name){
        this.name = name
    }
    static show(){
        console.log('123')
    }
}
class VipUser extends User{}
VipUser.show()                    // 123
User.show()                       // 123
var foo = new User('foo')
foo.show()                        // foo.show is not a function

(5)静态属性

  • class的静态属性指的是 Class 自己的属性,目前只能经过Class.propName定义静态属性
  • 静态属性能够被子类继承,不会被实例继承
class User{}
User.name = 'foo' // 为class定义一个静态属性

class VipUser extends User{}
console.log(VipUser.name)         // foo

var foo = new User()
console.log(foo.name)             // undefined

(6)私有属性和私有方法

es6是不支持私有属性和私有方法,可是平常需求可能会用到私有属性和私有方法,因此目前有一些提案,不过只是提案,还没有支持。

类的继承

ES5写法

function User(name,pass){
    this.name=name;
    this.pass=pass;
}
User.prototype.showName=function(){
    console.log(this.name);
};

/**
 * [showPass 获取用户密码]
 * @return {[type]} [返回用户密码]
 */
User.prototype.showPass=function(){
    console.log(this.pass);
};

//继承user类
function aUser(name, pass, type) {
    User.call(this, name, pass);
    this.type = type;
};

aUser.prototype.showType = function() {
    console.log(this.type);
};

var ul=new User('黄继鹏','abc');
ul.showName()//黄继鹏

var ull=new aUser('继小鹏','ccc','男');
ul.showName();//继小鹏
ull.showType();//男

//aUser继承类User类,而且有本身的方法

class经过extends关键字实现继承:

class User {
    constructor(name){
        this.name = name
    }
    show(){...}
}
class VipUser extends User{
    constructor(vipName){      // 子类的构造器
        super(vipName)         // 调用父类的constructor。至关于User.prototype.constructor.call(this,vipName)
    }
    showVip(){...}
}

var v = new VipUser('foo')     // 建立实例
v instanceof VipUser           // v是子类VipUser的实例
v instanceof User              // v仍是父类User的实例

(1)super

super能够当作函数使用,也能够当作对象使用。

当作函数使用
super做为函数调用时,表明父类的构造函数,就是在子类的构造器中执行父类的constructor函数以获取父类的this对象,由于子类没有本身的this对象,因此ES6规定子类必须在constructor中执行一次super函数。super()函数只能在子类的constructor中执行,不能在其余地方执行。

虽然super表明父类的构造器,可是super()在执行时内部的this指向子类,因此super()就至关于User.prototype.constructor.call(this)。

当作对象使用
super能够做为对象调用父类的属性和方法,在子类的普通方法中,指向父类的原型对象(即User.prototype);在子类的静态方法中,指向父类。

class User {
  constructor(){
    this.x = 'hello'
  }
  show() {
    return 2;
  }
}

class VipUser extends User {
  constructor() {
    super();
    console.log(super.show()); // 2  此时super指向User.prototype,至关于User.prototype.show()
    console.log(super.x)       // undefined  没法访问实例属性
  }
}

let vip = new VipUser();
console.log(vip.x);//hello

因为super对象在普通函数中使用super指向User.prototype,因此super只能访问父类的原型上的方法,无法访问父类的实例属性和实例方法。

ES6规定若是在子类中使用super对象调用父类的方法时,方法内部的this指向子类

class User {
    constructor() {
        this.x = 1
    }
    show() {
        return this.x;
    }
}

class VipUser extends User {
    constructor() {
        super();
        this.x = 2
        console.log(super.show())   // 2   此时show()方法内部的this指向子类,因此输出2,而不是1
    }
}

let vip = new VipUser();

上述代码中虽然super.show()调用的是User.prototype.show(),可是因为经过super对象调用父类方法时,方法内部的this指向子类,因此super.show()至关于 super.show().call(this),也就是User.prototype.show().call(this)

在子类的静态方法中super对象指向父类,而不是父类的原型(User.prototype)。

class User {
    constructor() {
        this.x = 1
    }
    static fn() {
        console.log('父类静态方法')
    }
}

class VipUser extends User {
    constructor() {
        super();
        this.x = 2
    }
    static childFn() {
        super.fn()       // 至关于User.fn()
    }
}

VipUser.childFn()

(2)类的prototype和proto属性

在es5中每个对象都有proto属性,指向对应的构造函数的prototype属性。Class 做为构造函数的语法糖,同时有prototype属性和proto属性,所以同时存在两条继承链。

  • 子类的proto属性,表示构造函数的继承,老是指向父类。
  • 子类prototype属性的proto属性,表示方法的继承,老是指向父类的prototype属性。
class User {
}

class VipUser extends User {
}

VipUser.proto === User // true
VipUser.prototype.proto === User.prototype // true

(3)实例的proto属性

子类实例的proto属性指向子类的原型(子类的prototype),子类实例的proto属性的proto属性指向父类的原型(父类的prototype)

class User {
}

class VipUser extends User {
}

var vip = new VipUser()

console.log(vip.proto === VipUser.prototype)           // true
console.log(vip.proto.proto === User.prototype)    // true

面向对象-应用

面向对象应用---react

react介绍:

  1. 组件化
    在react里一个组件就是一个class,
  2. 依赖于JSX
    jsx==babel==browser.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
        let oDiv=document.getElementById('div1');
        ReactDOM.render(
            <span>123</span>,
            oDiv
        );
    </script>
</head>
<body>
    <div id="div1"></div>
</body>
</html>

继小鹏

ReactDOM.render(
            <span>123</span>,
            oDiv
        );

这种语法为何会支持呢?

这个就是jsx和普通js最大的差异。

你能够认为jsx是普通js的扩展版本

既然是扩展版本,那确定会多出一些功能来。

若是不写引号,不是字符串同时长得像html,他就是能够要建立一个标签

切换搭配重点

react是一个基于组件

组件与class形式存在

我想写一个组件,做为一个组件是否是应该有一些基本的功能,好比我能被渲染,我有一些状态,我有生命周期,换句话说我如今不是从零开始写一个class,我须要不少基础的类的集成。

class Test extends React.Component{
    
}

类继承最大的意义在于一切不用从零开始

一个类须要有构造函数constructor.
做为继承类,在构造函数中须要继承父级的属性

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
class Test extends React.Component{
    //构造函数接到任何参数,都给继承父级类
    constructor(...args){
        super(...args);
    }
    render(){
        return <div>hello world</div>
    }
}

let oDiv=document.getElementById('div1');
ReactDOM.render(
    <Test>123</Test>,
    oDiv
);
</script>
</head>
<body>
    <div id="div1"></div>
</body>
</html>

image.png

组件套组件方法例子

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
class Item extends React.Component{
    //构造函数接到任何参数,都给继承父级类
    constructor(...args){
        super(...args);
    }
    render(){
        return <div>{this.props.str}</div>
    }
}

class List extends React.Component{
    //构造函数接到任何参数,都给继承父级类
    constructor(...args){
        super(...args);
    }
    render(){
        /*//写法1
        let aItem=[];
        for(let i=0;i<this.props.arr.length;i++){
            aItem.push(<Item key={i} str={this.props.arr[i]}></Item>);
        }*/
        /*// 写法2
        let aItem=this.props.arr.map((str,index)=>{
            return <Item key={index} str={str}></Item>
        })
        return <div>
            {aItem}
        </div>*/
        // 写法3
        return <div>
            {
                this.props.arr.map((str,index)=>{
                    return <Item key={index} str={str}></Item>
                })
            }
        </div>
    }
}

let oDiv=document.getElementById('div1');
ReactDOM.render(
    <List arr={['abc','efg','hij']}></List>,
    oDiv
);
</script>
</head>
<body>
    <div id="div1"></div>
</body>
</html>

Promise

Promise的中文含义是承诺

了解Promise以前。先来了解下同步异步

异步:操做之间没啥管系,同时进行多个操做
同步:同时只能作一件事

同步异步的优缺点

异步:代码更复杂
同步:代码简单

一个页面可能会有多个请求
好比淘宝网页,banner区域,侧边栏,导航栏,右侧栏,信息商品等
都是由镀铬接口异步请求组成

这就回形成代码逻辑复杂

按照以往前端ajax请求写法。一个请求成功后继续请求嵌套。逻辑会变得异常费劲

异步

$.ajax({
    type: 'post',
    url: '/api/banner',
    success:function(result){
        console.log('成功');
        $.ajax({
            type: 'post',
            url: '/api/1',
            success:function(result){
                console.log('成功');
                $.ajax({
                    type: 'post',
                    url: '/api/banner',
                    success:function(result){
                        console.log('成功');
                        $.ajax({
                            type: 'post',
                            url: '/api/banner',
                            success:function(result){
                                console.log('成功')
                            },
                            error:function(error){
                                console.log('失败')
                            },
                        })
                    },
                    error:function(error){
                        console.log('失败')
                    },
                })
            },
            error:function(error){
                console.log('失败')
            },
        })
    },
    error:function(error){
        console.log('失败')
    },
})

同步

let banner_data=ajax_async('/banner');
let banner_data1=ajax_async('/banner1');
let banner_data2=ajax_async('/banner2');
let banner_data3=ajax_async('/banner3');
let banner_data4=ajax_async('/banner4');

你会发现异步处理性能好,用户体验好,但实际代码复杂

要是同步方式页面用户体验很差

这个时候幻想一下,我能不能像同步方式来写代码。也像异步同样请求数据。

Promise就能作到这个工做

Promise--消除异步操做

  • 用同步书写方式,来书写异步方法

Promise如何使用

须要使用promise的时候,你须要new一个promise对象。

这个对象接收一个参数,是一个函数。
将异步的代码写在函数里

这个函数两个参数
resolve决心
reject拒绝

//封装Promise ajax
let p=new Promise(function(resolve,reject){
    //异步代码块
    //resolve--成功了
    //reject--失败了
    $.ajax({
        type: 'post',
        dataType:'json',
        url: '/api/banner',
        success:function(result){
            resolve(result);
        },
        error:function(error){
            reject(error);
        },
    })
});

//使用Promise ajax封装
//当Promise调用有结果了就会调用then
//then有两个参数,都是函数,第一个是resolve,第二个是reject
p.then((result)=>{
    console.log(result);
},(error)=>{
    console.log(error);
})
function createPromise(url){
    return new Promise(function(resolve,reject){
        //异步代码块
        //resolve--成功了
        //reject--失败了
        $.ajax({
            type: 'post',
            dataType:'json',
            url,
            success:function(result){
                resolve(result);
            },
            error:function(error){
                reject(error);
            },
        })
    });
}


createPromise('./aa')
.then((res)=>{
    console.log(res)
},(err)=>{
    console.log(err)
})
function createPromise(url){
    return new Promise(function(resolve,reject){
        //异步代码块
        //resolve--成功了
        //reject--失败了
        $.ajax({
            type: 'post',
            dataType:'json',
            url,
            success:function(result){
                resolve(result);
            },
            error:function(error){
                reject(error);
            },
        })
    });
}

Promise.all([
    createPromise('./aa'),
    createPromise('./bb')
])
.then((res)=>{
    let [arr1,arr2]=res
},(err)=>{
    console.log(err)
})

generator-认识生成器函数

generator的做用

generator-生成器

生成器是程序里面的一个概念,能够依靠它生成一堆东西

Generator能够理解为生成器,和普通函数没多大区别,普通函数是只要开始执行,就一直执行到底,而Generator函数是中间能够停,搭配使用next函数继续执行。

生动的比喻

普通函数比如坐飞机,飞机起飞,不到目的地中途是不会降落的

Generator比如于出租车。能够随叫随停。停了再走,走了再停

(1)定义一个Generator函数

function * fn(){
    alert('a')
    yield
    alert('b')
}

var f = fn()
f.next()  // a
f.next()  // b

直接调用Generator函数,是什么都不执行的,调用第一个next()才开始执行,一直执行到第一个yield中止,第二次调用next(),从第一个yield执行到第二个yield中止,依次类推

如今疑惑,在真实场景中,我为何要让一个函数停呢?

刚才举个了出租车的例子,说白了,你为何要让出租车司机停车,确定是你有事情,你要去忙,或者要求拿什么东西,或者见什么朋友。
等你事情办完了,还再回来。

因此Generator特别适合一个场景。

好比说你要请求数据。请求数据不是瞬间就能回来的,这个时候就须要暂停,来等他结果过来。再继续执行下面的操做。

/**
 * 普通函数在执行过程当中须要请求获得结果再执行对应代码,就会出现代码嵌套再嵌套
 */
function 函数(){
    代码...

    ajax({
        代码...
    })
}

/**
 * Generator函数可让代码在那一步暂时暂停 拿到数据后再继续往下走
 */
function *函数(){
    代码...

    yiels ajax(xxx)

    代码...
}

Generator是怎么作到走走停停的?

其实本质是用Generator函数生成了一堆小函数

比方说fn函数

function * fn(){
    alert('a')
    yield
    alert('b')
}

var f = fn()
f.next()  // a
f.next()  // b

其实他在背后生成了两个小函数

function fn_1(){
    alert('a')
}
function fn_2(){
    alert('b')
}

固然这个过程咱们是看不见的
至关于把一个大函数切分红了两个小函数

第一次next的时候他走的是fn_1

第二次next的时候走的是fn_2

generator-yield

yield和next

yield表明暂时暂停执行,next表明继续执行。

yield和next能够传参数,也能够有返回值

  1. yield能够传参
function *show(){
    console.log('a')
    let a=yield;
    console.log('b')
    console.log(a)
}
let gen=show();
gen.next(12)
gen.next(5)

//a
//b
//5

继小鹏

第一次执行next的时候执行黄色框代码
第二次执行红色框的代码

传参的时候经过yield来传参的时候,第一个next是无效的,
若是想给第一个过程传参须要使用传统方法,在使用函数时传参

function *show(num1,num2){
    console.log(`${num1},${num2}`)
    console.log('a')
    let a=yield;
    console.log('b')
    console.log(a)
}
let gen=show(11,12);
gen.next(12);//无法给yield传参
gen.next(5)

//11,12
//a
//b
//5
  1. yield返回
function *show(){
    console.log('a')
    let a=yield 12;
    console.log('b')
}
let gen=show(11,12);
let res1=gen.next();
console.log(res1)
let res2=gen.next()
console.log(res2)


//a
//{value: 12, done: false}
//b
//{value: undefined, done: true}

value是yield 返回的参数
done代码函数是否走完

为何第二次执行完value是空

由于第二次next是执行的最后一道程序,最后一道程序就没有yield 了,若是想返回东西须要使用return

function *show(){
    console.log('a')
    let a=yield 12;
    console.log('b')
    return 111;
}
let gen=show(11,12);
let res1=gen.next();
console.log(res1)
let res2=gen.next()
console.log(res2)


//a
//{value: 12, done: false}
//b
//{value: 111, done: true}

yield 究竟是个啥

继小鹏

继小鹏

generator-实例:runner

这种Generator函数适用多个异步请求之间有逻辑分析的状况,好比有一个需求,先请求用户数据,根据用户数据的类型判断用户是普通用户仍是VIP用户,而后再根据判断结果请求普通商品数据或者VIP商品数据

// 借助runner脚本,runner脚本规定Generator函数执行完一个next以后自动执行下一个next
runner(function() * (){
    let userData = yield $.ajax(...) // 请求用户数据
    if(userData.type === 'vip') {
        let goods = yield $.ajax(...) // 请求vip商品数据
    } else {
        let goods = yield $.ajax(...) // 请求普通商品数据
    }
})

继小鹏

第一次yield ajax实际上是Promise对象,将Promise对象yield 出去。
yield 给了runner对象

将数据请求完成给data1

这个函数暂停了

使用Generator函数使得代码看起来更像同步代码,其实使用Promise一样能够实现这种效果,只不过得须要在then()函数中嵌套请求。

异步请求的几种方式

  1. 回调写法
$.ajax({
    type: 'post',
    url: '/api/banner',
    success:function(result){
        console.log('成功');
        $.ajax({
            type: 'post',
            url: '/api/1',
            success:function(result){
                console.log('成功');
                $.ajax({
                    type: 'post',
                    url: '/api/banner',
                    success:function(result){
                        console.log('成功');
                        $.ajax({
                            type: 'post',
                            url: '/api/banner',
                            success:function(result){
                                console.log('成功')
                            },
                            error:function(error){
                                console.log('失败')
                            },
                        })
                    },
                    error:function(error){
                        console.log('失败')
                    },
                })
            },
            error:function(error){
                console.log('失败')
            },
        })
    },
    error:function(error){
        console.log('失败')
    },
})
  1. Promise写法
function createPromise(url){
    return new Promise(function(resolve,reject){
        //异步代码块
        //resolve--成功了
        //reject--失败了
        $.ajax({
            type: 'post',
            dataType:'json',
            url,
            success:function(result){
                resolve(result);
            },
            error:function(error){
                reject(error);
            },
        })
    });
}

Promise.all([
    createPromise('./aa'),
    createPromise('./bb')
])
.then((res)=>{
    let [arr1,arr2]=res
},(err)=>{
    console.log(err)
})
  1. Generator写法
runner(function() * (){
    let userData = yield $.ajax(...) // 请求用户数据
    if(userData.type === 'vip') {
        let goods = yield $.ajax(...) // 请求vip商品数据
    } else {
        let goods = yield $.ajax(...) // 请求普通商品数据
    }
})

Promise和Generator相比,Generator并无特别的省事

Promise也有它不适用的地方。我若是是写死要请求接口。那么Promise和Generator确实没太大区别,

Generator他的优势在于适合参杂一些逻辑

比方说在请求一个接口拿到用户信息,根据信息判断他该去请求哪些不一样的接口

继小鹏

感受比普通嵌套还麻烦

带逻辑-Generator

// 借助runner脚本,runner脚本规定Generator函数执行完一个next以后自动执行下一个next
runner(function() * (){
    let userData = yield $.ajax(...) // 请求用户数据
    if(userData.type === 'vip') {
        let goods = yield $.ajax(...) // 请求vip商品数据
    } else {
        let goods = yield $.ajax(...) // 请求普通商品数据
    }
})

Promise适合一次请求一堆场景
Generator适合逻辑性请求处理

generator-实例:KOA

KOA是nodejs的框架

继小鹏

async await

async其实就是对Generator的封装,只不过async能够自动执行next()。

async function read () {
    let data1= await new Promise(resolve => {
        resolve('100')
    })
    let data2 = await 200

return 300
}

async 返回值

async默认返回一个Promise,若是return不是一个Promise对象,就会被转为当即resolve的Promise,能够在then函数中获取返回值。

async必须等到里面全部的await执行完,async才开始return,返回的Promise状态才改变。除非遇到return和错误。

async function fn () {
    await 100
    await 200
    return 300
}
fn().then(res => {
    console.log9(res) // 300
})

await

await也是默认返回Promise对象,若是await后面不是一个Promise对象,就会转为当即resolve的Promise

若是一个await后面的Promise若是为reject,那么整个async都会中断执行,后面的awiat都不会执行,而且抛出错误,能够在async的catch中捕获错误

async function f() {
  await Promise.reject('error');
  await Promise.resolve('hello world'); // 不会执行
}
f().then(res =>{

}).catch(err=>{
    console.log(err)  // error
})

若是但愿一个await失败,后面的继续执行,可使用try...catch或者在await后面的Promise跟一个catch方法:

// try...catch
async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))   // hello world

// catch
async function f() {
  await Promise.reject('出错了')
    .catch(e => console.log(e));   // 出错了
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))  // hello world
相关文章
相关标签/搜索