zoukankan      html  css  js  c++  java
  • ES6 class 类的理解(一)

    优点

    ES6 的类提供了几点明显的好处:

    兼容当前大量的代码。
    相对于构造器和构造器继承,类使初学者更容易入门。
    子类化在语言层面支持。
    可以子类化内置的构造器。
    不再需要继承库;框架之间的代码变得更加轻便。
    为将来的高级特性奠定了基础: traits (或者 mixins ), 不可变实例,等等。
    使工具能够静态分析代码( IDE ,类型检测器,代码风格检测器,等等)。

    缺点

    ES6 类掩盖了 JavaScript 继承的本质;
    类会禁锢你,因为强制性的 new。

    传统的类

    function Point(x, y){
      	this.x = x;
        this.y = y;
    }
    Point.prototype.toString = function(){
        return "(" + this.x + "," + this.y + ")";
    }
    const p = new Point(1,2);
    console.log(p);//Point {x: 1, y: 2}
    console.log(p.toString());//(1,2)
    

    主要缺点是,比较复杂,用到了this和prototype,编写和阅读都很费力。
    上面的式子可以如下简单的理解:

    用构造函数模拟"类",在其内部用this关键字指代实例对象。

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

    生成实例的时候,使用new关键字。

    const p = new Point(1,2);
    

    然后将类的属性和方法,定义在构造函数prototype对象上

    Point.prototype.toString = function(){
    
        return "(" + this.x + "," + this.y + ")";
        
    }
    

    ES6的class写法就相当于语法糖

    上面代码的改用class来写

    class Points {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString(){
            return '(' + this.x + ',' + this.y + ')';            }
    }
    const ps = new Points(1, 2);
    console.log(ps);//Points {x: 1, y: 2}
    console.log(ps.toString());//(1,2)
    

    ES6的类可以看作构造函数的另一种写法

    class Cty{
        //....
    }
    console.log(typeof Cty);//function
    console.log(Cty === Cty.prototype.constructor);//true
    //类的数据类型是函数,类本身就指向构造函数
    

    上面可以理解为:类的数据类型是函数,类本身就指向构造函数

    使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致

    class Bar {
        doStuff(){
            console.log('stuff');
        }
    }
    const b =new Bar();
    b.doStuff();//stuff
    

    类的实例上面的方法,其实就是调用原型上的方法

    class B {};
    const BS = new B();
    console.log(BS.constructor === B.prototype.constructor);//true
    

    类与子类

    class Poin{
        constructor(x,y){
            this.x = x;
            this.y = y;
        }
        toString(){
            return `(${this.x},${this.y})`;
        }
    }
    class ColorPoin extends Poin{
        constructor(x,y,color){
            super(x,y);
            this.color = color;
        }
        toString(){
            return super.toString() + " in " + this. color;
        }
    }
    // 类型
    console.log(typeof Poin);//function
    //news实例
    const cp = new ColorPoin(25,8,'green');
    console.log(cp.toString());//(25,8) in green
    console.log(cp instanceof ColorPoin);//true
    console.log(cp instanceof Poin);//true
    // instanceof测试构造函数的prototype属性是否出现在对象的原型链中的任何位置
    

    Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系(instanceof测试构造函数的prototype属性是否出现在对象的原型链中的任何位置)。

    下面是一些方法:

    Object.assign方法可以很方便的一次像类添加多个方法

    class ObjAssign {
    	constructor(name, age){
            this.name = name;
            this.age = age;
        }
    }
    Object.assign(ObjAssign.prototype,{
        toString(){
            console.log("string");
        },
        toValue(){
            console.log("value")
     		}
    })
    const Obj = new ObjAssign('Bob',24);
    console.log(Obj);
    Obj.toString();//string
    Obj.toValue();//value
    console.log(Object.keys(ObjAssign.prototype));//["toString", "toValue"]
    console.log(Object.getOwnPropertyNames(ObjAssign.prototype));// ["constructor", "toString", "toValue"]
    

    类的实例

     class Pott {
        constructor(x,y){
            this.x = x;
            this.y = y;
        }
        toString() {
            return '(' + this.x + ',' + this.y + ')';
        }
    }
    const pott = new Pott(2,3);
    pott.toString();
    console.log(pott.hasOwnProperty("x"));//true
    console.log(pott.hasOwnProperty("y"));//true
    console.log(pott.hasOwnProperty("toString"));//false
    console.log(pott);
    console.log(pott.__proto__);
    console.log(pott.__proto__.hasOwnProperty("toString"));//true
    
    const p1 = new Pott(2,3);
    const p2 = new Pott(3,3);
    
    console.log(p1.__proto__ === p2.__proto__);//true
    
    p1.__proto__.printName = function(){
        return "Oops";
    }
    console.log(p1.printName());//Oops
    console.log(p2.printName());//Oops
    const p3 = new Pott(4,2);
    console.log(p3.printName());//Oops
    

    取值函数(getter)和存值函数(setter)

    prop属性有对应的存值函数和取值函数

    class MyClass {
       constructor(){
            //...
        }
        get prop(){
            return 'getter';
        }
        set prop(value){
            console.log("setter:" + value);
        }
    }
    const inst = new MyClass();
    inst.prop = 123;//setter: 123
    console.log(inst.prop)//getter
    

    存值函数和取值函数是设置在属性的Descriptor对象上的

    class CustomHTMLElement {
       constructor(element) {
            this.element = element;
        }
    
        get html() {
            return this.element.innerHTML;
        }
    
        set html(value) {
            this.element.innerHTML = value;
        }
    }
    
    const descriptor = Object.getOwnPropertyDescriptor(
        CustomHTMLElement.prototype, "html"
    );
    
    console.log("get" in descriptor)  // true
    console.log("set" in descriptor)  // true
    

    calss 表达式

    const MyCl = class Me {
        getClassName() {
            return Me.name;
        }
    }
    const inMe = new MyCl();
    console.log(inMe.getClassName());//Me  只在class内部有定义
    

    person立即执行实例

    const person = new class{
        constructor(name){
            this.name = name;
        }
        sayName(){
            console.log(this.name);
        }
    }('张三');
    person.sayName();//张三
    

    class name 属性

    class Mine {
    //...
    }
    console.log(Mine.name);//Mine
    

    class this的指向问题

    this.printName = this.printName.bind(this)绑定解决

    class Logger{
       constructor(){
            this.printName = this.printName.bind(this);
        }
        printName(name = 'there'){
            this.print(`Hello ${name}`);
        }
        print(text){
            console.log(text);
        }
    }
    const logger = new Logger();
    const {printName} = logger;
    printName();//Hello there
    

    静态方法static

    如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是通过类来调用

    class Foo{
        static classMethod() {
            return 'hello';
        }
    }
    console.log(Foo.classMethod());//Hello
    
    const foo = new Foo();
    // console.log(foo.classMethod())//foo.classMethod is not a function
    
    class Fun {
       static bar(){
           this.baz();
       }
       static baz(){
           console.log('hello');
       }
       baz(){
           console.log('world');
       }
    }
    Fun.bar();//hello
    

    父类静态方法可以被子类调用

    class Func{
       static classMethod() {
            return 'hello';
        }
    }
    class Baa extends Func{
        static classMethod(){
            console.log(super.classMethod + ",too") ;
        }
    }
    Baa.classMethod();//hello,too
    

    实例属性的新写法

    class IncreasingCounter{
        // constructor(){
        //     this._count = 0;
        // }
        _count = 0;
        get value(){
            console.log('getting the current value');  
            return this._count;           
        }
        increment(){
            this._count++;
        }
    }
    

    new.target属性

    确保函数只能通过new命令调用

    function PersonMan(name){
        if(new.target !== undefined){
            this.name = name;
        }else{
            throw new Error('必须使用new命令生成实例')
        }
    }
    function PersonWoman(name){
        if(new.target === PersonWoman){
            this.name = name;
        }else{
            throw new Error('必须使用new命令生成实例')
        }
    }
    const personman = new PersonMan('张三');
    const personwoman = new PersonWoman('张三');
    // const personwoman2 =  PersonWoman.call(PersonWoman,'张三');//报错
    

    内部调用new.target会返回当前的class

    class Rectangle{
        constructor(length,width){
            console.log(new.target);
            console.log(new.target===Rectangle);
            this.length = length;
            this.width = width;
        }
    }
    const rectangle = new Rectangle(3,4);
    

    子类继承父类时,new.target会返回子类

    class Rec{
       constructor(length,width){
            console.log(new.target);
            console.log(new.target===Rectangle);
            console.log(new.target===Square);
            this.length = length;
            this.width = width;
            //...
        }
    }
    class Square extends Rec{
        constructor(length,width){
            super(length,width);
        }
    }
    const squareA = new Square(3,6);//false/true
    

    注意:Object.create()法

    Javascript的国际标准ECMAScript第五版(目前通行的是第三版),提出了一个新的方法Object.create()。
    使用Object.create()的方法,"类"就是一个对象,不是函数。
    详细的可以看这篇文章:Javascript定义类(class)的三种方法

    参考文章
    探索ES6
    ES6-阮一峰
    Javascript定义类(class)的三种方法-阮一峰

  • 相关阅读:
    获取经纬度 CLLocation
    获取手机 IP
    UIBeaierPath 与 CAShapeLayer
    导航栏转场动画CATransition
    UITextField输入限制/小数/首位等
    程序进入后台继续执行
    发送短信
    网络AFNetworking 3.1
    UIBezierPath
    CoreAnimation 核心动画 / CABasicAnimation/ CAKeyframeAnimation
  • 原文地址:https://www.cnblogs.com/yihan123/p/12658077.html
Copyright © 2011-2022 走看看