美文网首页
JavaScript进阶-2

JavaScript进阶-2

作者: Harper324 | 来源:发表于2018-12-23 17:53 被阅读0次
1、ES6基础

ECMAScript(ES6) 是JavaScript语言的下一代标准,已经在2015年6月正式发布了;在 JavaScript 的基础上做了重大的更新,提供了更优雅的语法和特性。ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的ECMAScript 方言还有 Jscript 和 ActionScript )。
ES6 新特性

(1)let 和 const
  • let 和 const 相比于 var 声明的变量有块作用域,let 和 const 只在于当前的块作用域中有效。而 var 声明的变量是在函数作用域内有效。
  • const 和 let 的区别则在于, const 声明变量的同时必须立即给一个初始值,且无法在对该值进行修改。如果该初始值是一个对象,那么该对象的值是可以被修改的。
{
  let a = 1;  // a 只能在当前块中被访问
  var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
(2)变量的解构赋值

按照一定的模式,从数组或对象中提取值,对变量进行赋值的操作。

  • 数组的解构
var [a,b,c] = [1,2,3] // 结果 a=1 b=2 c=3
var [a,[b,c]] = [1,[2,3]] // 结果 a=1 b=2 c=3

//解构不成功
var [a] = [];  // a = undefined   
var [a, b] = [1]; //a=1 b=undefined

//不完全解构
var [a] = [1,2]; //a=1

//默认值
var [a=1] = [] //a=1

//如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错
let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};
  • 对象的解构
var { b, a } = { a: "aaa", b: "bbb" };
a // "aaa"
b // "bbb"
(3)字符串拓展
  • 模板字符串
    模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串(类似 pre 标签的作用),或者在字符串中嵌入变量。
// 普通字符串 想要换行需要加上 '\n'
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

// 字符串中嵌入变量
var name = "ac", time = "today";
`Hello ${name}, how are you ${time}?` // Hello ac, how are you today? 
  • rest 展开运算符
function example(...values){
    console.log(values)// console: [1,2,3,4]
}

example(1,2,3,4) 


var a = [1,2,3]

var b = [...a,4,5,6] //b = [1,2,3,4,5,6]
(4)箭头函数
  • ES6 提供了新的方式 => 来定义函数
var func = parm => parm
等同于
var func = function (parm){
    return parm
}
  • 如果函数没有参数或有多个参数,那么:
var func = () => //some code
等同于
var func = function (){
    some code
}

var func = (parm1,parm2) => //some code
等同于
var func = function (parm1,parm2){
    some code
}
  • 如果箭头函数的函数体只包含一行代码,则可以不需要写大括号以及 return 语句返回(如果有返回值)
var sum = (num1,num2) => num1+num2
等同于
var sum = (num1,num2) => {return num1+num2}
等同于
var sum = function(num1,num2){return num1+num2}
  • 箭头函数使得表达更加简洁
[1,2,3].map( item=> 2 * item)
等同于
[1,2,3].map(function(item){
    return item * 2
})

[1,3,2].sort((a,b) => a - b)
  • 箭头函数中的this总是指向词法作用域,也就是外层调用者obj
var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25
2、面向对象编程
1、封装

(1)生成实例对象的原始模式

var Cat = {
    name : '',
    color : ''
} // 原型对象

// 生成两个实例对象
var cat1 = {};
cat1.name = "大毛“;
cat1.color = "黄色";

var cat2 = {};
cat2.name = "二毛";
cat2.color = "黑色";

这就是最简单的封装,把两个属性封装在一个对象里。缺点:多个对象书写较麻烦;看不出实例与原型间的联系

(2)工厂模式

function Cat(name, color) {
    return {
        name:name,
        color:color
    }
}

//生成实例对象,等于调用函数
var cat1 = Cat("大毛","黄色");
var cat2 = Cat("二毛","黑色");

缺点:cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例

(3)构造函数(Constructor)模式

构造函数就是普通函数,但是在内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

function Cat(name,color){
    this.name = name;
    this.color = color;
}

// 生成实例对象
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");

// cat1和cat2会自动含有constructor属性
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true

//instanceof运算符,验证原型对象和实例对象之间的关系
alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true

//相同的属性和方法占用内存不同
alert(cat1.eat == cat2.eat);//false

缺点:相同的属性和方法会占用不同的内存。

(4)Prototype模式

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,这个对象的所有属性和方法,都会被构造函数的实例继承。因此,可以把那些不变的属性和方法直接定义在prototype对象上。

function Cat(name, color) {
    this.name = name;
    this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){
    alert("吃老鼠")
};

//生成实例
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); //猫科动物
cat1.eat(); //吃老鼠

//相同属性和方法占用同一个内存
alert(cat1.eat == cat2.eat); //true

2、继承

(1)使用callapply方法将父对象的构造函数绑定在子对象上:

function Cat(name,color){
    Animal.apply(this,arguments);
    this.name = name;
    this.color = color;    
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); //动物


(2)prototype模式

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); //动物

(3)直接继承prototype

function Animal() {}
   Animal.prototype.species = "动物";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); //动物)

缺点:Cat.prototype和Animal.prototype指向同一个对象,任何对Cat.prototype的修改都会反映到Animal.prototype上

(4)利用空对象作为中介

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;// 修改Cat的prototype对象不会影响到Animal的prototype对象
alert(Animal.prototype.constructor); //Animal

//将上面的方法封装成一个函数,便于使用
function extend(Child, Parent) {
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
}

//使用
extend(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); //动物

(5)拷贝继承

//把Animal的所有不变属性放到prototype对象上
function Animal(){}
Animal.prototype.species = "动物";

//用函数来实现属性拷贝目的
function extend2(Child,Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
        c[i] = p[i];
    }
    c.uber = p;
}

//使用
extend2(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); //动物
3、非构造函数的继承

(1)object()方法

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

//第一步先在父对象的基础上,生成子对象: 
 var Doctor = object(Chinese);
//然后,再加上子对象本身的属性:
  Doctor.career = '医生';
//子对象已经继承了父对象的属性了
  alert(Doctor.nation); //中国

(2)浅拷贝

 function extendCopy(p) {
    var c = {};
    for (var i in p) { 
      c[i] = p[i];
    }
    c.uber = p;
    return c;
  }

//使用的时候,这样写:

  var Doctor = extendCopy(Chinese);
  Doctor.career = '医生';
  alert(Doctor.nation); // 中国

缺点:如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
(3)深拷贝

function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

//使用的时候这样写:
  var Doctor = deepCopy(Chinese);
4、Class 的基本语法

(1)类的定义(类的数据类型就是函数,类本身就指向构造函数),使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

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

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  • Object.assign()方法可以很方便地一次向类添加多个方法
class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});
  • 类必须使用new调用,否则会报错。
  • 实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。
//定义类
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

上面代码中,x和y都是实例对象point自身的属性(因为定义在this变量上),所以hasOwnProperty方法返回true,而toString是原型对象的属性(因为定义在Point类上),所以hasOwnProperty方法返回false。

  • 取值函数(getter)和存值函数(setter):在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'
  • 属性表达式:类的属性名,可以采用表达式。
let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}
  • Class 表达式
const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};//这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类

采用 Class 表达式,可以写出立即执行的 Class

let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}('张三');

person.sayName(); // "张三"

(2)静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
  • 如果静态方法包含this关键字,这个this指的是类,而不是实例
  • 静态方法可以与非静态方法重名。
  • 父类的静态方法,可以被子类继承。
  • 静态方法也是可以从super对象上调用的。
5、Class 的继承

Class 可以通过extends关键字实现继承,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

子类必须在constructor方法中调用super方法

class Point {
}
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}


相关文章

网友评论

      本文标题:JavaScript进阶-2

      本文链接:https://www.haomeiwen.com/subject/iljqkqtx.html