JS对象模型,this,bind
高阶对象Mixin
Promise

JS对象模型


JavaScript 是一种基于原型(Prototype)的面向对象语言,而不是基于类的面向对象语言。原型对象就是一个模板,新的对象从这个模板构建从而获取最初的属性。任何对象在运行时可以动态的增加属性。而且,任何一个对象都可以作为另一个对象的原型,这样后者就可以共享前者的属性

字面值创建对象

// key值只能是字符串类型,最终都会转换成字符串
// 引号可以省略
const obj={
    a: 100,
    'b': 200,
    1: 300
};

console.log(obj);

let a = 1, b=2;
let obj1 = {a, b};
console.log(obj1);

let obj2 = {c:a, d:b};
console.log(obj2);

let e = 'abc'
let obj3 = {
    e:1000,  // 这里的e是字符串e
    [e]:3000 // 如果要用e变量,就要把e用中括号括起来
};
console.log(obj3);
--------------输出结果--------------------
{ '1': 300, a: 100, b: 200 }
{ a: 1, b: 2 }
{ c: 1, d: 2 }
{ e: 1000, abc: 3000 }

ES6之前的构造器

  • 定义一个函数(构造器)对象,函数名首字母大写
  • 使用this定义属性
  • 使用new和构造器创建一个新对象
function Point(x, y) {
    this.x = x;
    this.y = y;
    this.show = () => { console.log(this, this.x, this.y) };
    console.log('Point...')
};

console.log(Point);
p = new Point(1, 2); //使用new
console.log(p);
p.show();


function Point3D(x, y, z) {
    Point.call(this, x, y); // "继承"
    this.z = z;
    console.log('Point3D...');
};
console.log(Point3D);
p1 = new Point3D(4, 5, 6); //使用new
console.log(p1);
p1.show();

new 构建一个新的通用对象,new操作符会将新对象的this值传递给构造器函数
如果不使用new关键字,就是一次普通的函数调用,this不代表实例

ES6中的class

从ES6开始,新提供了class关键字

  • 类定义使用class关键字。创建的本质上还是函数,是一个特殊的函数
  • 一个类只能拥有一个名为constructor的构造器方法。如果没有显式的定义一个构造方法,则会添加一个默认的constuctor方法
  • 继承使用extends关键字
  • 一个构造器可以使用super关键字来调用一个父类的构造函数
  • 类没有私有属性
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        // this.show = function () {console.log(this,this.x,this.y)};
        // 箭头函数
        this.show = () => console.log(this, this.x, this.y);

    };
    // 方法
    show() {
        console.log(this, this.x, this.y, '++++++++');
    }
};

// let p = new Point(1, 2); // 如果不new的话则会报错,TypeError: Class constructor Point cannot be invoked without 'new'
// p.show();


class Point3D extends Point {
    constructor(x, y, z) {
        super(x, y); // super
        this.z = z;
        // this.show = () => console.log(this, this.x, this.y, this.z);
    }
    show() { // 重写
        console.log(this, this.x, this.y, this.z, '-------');
    }

}
let p2 = new Point3D(4, 5, 6);
p2.show();

运行上面的示例,放开不同组合的同名属性和方法,看看子类和父类的运行情况
结论:

  • 父类、子类使用同一种方式类定义属性或者方法,子类覆盖父类
  • 访问同名属性或方法时,优先使用属性

静态方法

  • 在方法名前加上static,就是静态方法
class Add {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    // 静态方法
    static print() {
        console.log(this, this.x, '~~~~~'); // 静态方法中的this是Add类,而不是Add的实例
    }
}
add = new Add(40, 50);
console.log(Add);
Add.print();
// add.print(); // 实例不能访问直接访问静态方法,和C++、Java一致
add.constructor.print(); // 实例可以通过constructor访问静态方法
--------------------输出结果----------------------
[Function: Add]
[Function: Add] undefined ~~~~~
[Function: Add] undefined ~~~~~

静态的概念和Python的静态不同,相当于Python中的类变量

this问题

var student = {
    name: 'jerry',
    getNameFunc: function () {
        console.log(this.name);
        console.log(this);
        return function () {
            console.log(this, '~~~~') // this是global对象
            console.log(this.name, '~~~~~') // undefined //global对象没有name属性
            console.log(this == global); // true // this是global对象
            return '~~~~func return~~~~'
        };
    }
}

console.log(student.getNameFunc()())

问题:调用return 的匿名函数是直接调用的,是普通函数调用,所以this是全局对象

this问题解决

  • ES5引入bind方法
    • bind方法来设置函数的this值
var student = {
    name: 'jerry',
    getNameFunc: function () {
        console.log(this.name);
        console.log(this);
        return function () {
            console.log(this, '~~~~');
            console.log(this.name, '~~~~~');
            console.log(this == global);
            return '~~~~func return~~~~'
        };
    }
}

console.log((student.getNameFunc().bind(student))); // [Function: bound ] //bind绑定后返回新的函数
console.log('---------------------------------------');
console.log((student.getNameFunc().bind(student)()));

bind方法是为函数先绑定this,调用时直接用,区别于老语法的apple和call方法

  • 说明

    • 还可以通过显示传入,或使用apply、call方法来传入(老语法,目前用bind较多),这里不再举
    • apply、call方法都是函数对象的方法,第一参数都是传入this;apply传其他参数需要使用数组;call传其他参数需要使用剩余参数收集
  • ES6引入this的箭头函数

    • 使用this箭头函数新语法,则不存在兼容this问题
class Student {
    constructor() {
        this.name = 'jerry'
    };
    getNameFunc() {
        console.log(this.name);
        console.log(this, typeof (this));
        return () => {
            console.log(this, '~~~~');
            console.log(this.name, '~~~~~');
            console.log(this == global);
            return '~~~~func return~~~~';
        };
    }
}

s = new Student();
console.log(s.getNameFunc()())
---------------输出结果-------------
jerry
Student { name: 'jerry' } object
Student { name: 'jerry' } ~~~~
jerry ~~~~~
false
~~~~func return~~~~

高阶对象


  • 高阶对象(类):可以将多个类或对象混合成一个类或对象(Mixin)

高阶对象的实现

  • 将类的继承构建成箭头函数
// 普通类继承
class A extends Object { };
console.log(A);

// 匿名类
const B = class {
    constructor(x) {
        this.x = x;
    };
};
console.log(B);
console.log(new B(100));

// 匿名继承
const C = class extends Object {
    constructor() {
        super();
        console.log('C init~~~~~')
    }
}
console.log(C);
console.log(new C());

// 箭头函数,参数是类,返回值也是类
// 把上例中的Object看成参数
const x = (Sup) => {
    return class extends Sup {
        constructor() {
            super();
            console.log('C constructor');
        };
    };
}
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
// 上例可改写为
const DMixin = Sup => class extends Sup {
    constructor() {
        super();
        console.log('dmixin constructor');
    };
}

//cls = new DMixin(Object); // 不可以new,因为D是一个普通函数,它的返回值是一个带constructor的类
cls = DMixin(A); // 调用它返回一个类,一个带constructor的class
console.log(cls);
d = new cls();
console.log(d);

即实现了:传入一个基类,然后通过基类继承构造一个新的类
React框架大量使用了这种Mixin技术

Promise


  • ES6开始支持,用于处理异步请求
  • Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示

语法

// 语法
new Promise(
    /* 下面定义的函数是executor */
    function (resolve, reject) {...}
);
  • executor
    • executor 是一个带有 resolve 和 reject 两个参数的函数,这两个参数由Promise注入
    • executor 函数在Promise构造函数返回新建对象前被调用
    • executor中,resolve或reject只能执行其中一个函数
    • executor 内部通常会执行一些异步操作,一旦完成,可以调用resolve函数来将promise状态改成fulfilled,或者在发生错误时将它的状态改为rejected
    • 如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略
  • Promise的状态
    • pending: 初始状态
    • fulfilled: 操作成功完成
    • rejected: 操作失败

异步

setInterval和setTimeout

  • setInterval(function[, delay])间隔多少毫秒就执行函数一次,循环执行
  • setTimeout(function[, delay])等待多少毫秒就执行函数一次后结束

delay // 延时,缺省0,立即执行;function // 延时到的时候执行的函数

var myPromise = new Promise(function (resolve, reject) {
    console.log('do sth.')
    setTimeout(() => {
        console.log('~~~~~')
        resolve('ok'); // 只会执行其中一个
        reject('error');
    }, 3000); // 延时3秒执行一次结束
});

console.log(myPromise, '------------');

setInterval(() => {
    console.log(myPromise, '++++');
}, 1000); // 每隔1秒执行一次
-------------输出结果-----------
do sth.
Promise { <pending> } ------------
Promise { <pending> } ++++
Promise { <pending> } ++++
~~~~~
Promise { 'ok' } ++++
Promise { 'ok' } ++++
Promise { 'ok' } ++++
Promise { 'ok' } ++++
......
......

then和catch

  • Promise.then(onFulfilled, onRejected)
    • then的返回值是一个新的promise对象
    • Promise.then函数参数是2个函数,根据当前Promise对象的状态来调用不同的函数,如果当前Promise对象状态是fulfilled(即调用resolve函数)则走onFulfilled函数,相反,如果是rejected走onRejected函数
    • onFulfilled即成功回调函数,onRejected即失败回调函数,执行任意一个回调函数,对这个promise对象来说就是其返回值
var myPromise = new Promise(function (resolve, reject) {
    console.log('do sth.')
    setTimeout(() => {
        console.log('~~~~~')
        // resolve('ok'); // 如果fulfilled,看看结果
        reject('error');
    }, 3000);
});

console.log(myPromise, '------------');

setInterval(() => {
    console.log(myPromise, '++++');
}, 1000);

var pro1 = myPromise.then(
    value => {
        console.log('success:', value);
        return 'success ~~~~~';
    },

    reason => {
        console.log('fail:', reason);
        return 'fail ~~~~~';
    }
);

setInterval(() => {
    console.log(pro1, '=========');
}, 1000);
-----------------输出结果-----------------
do sth.
Promise { <pending> } ------------
Promise { <pending> } ++++
Promise { <pending> } =========
Promise { <pending> } ++++
Promise { <pending> } =========
~~~~~
fail: error
Promise { <rejected> 'error' } ++++
Promise { 'fail ~~~~~' } =========
Promise { <rejected> 'error' } ++++
Promise { 'fail ~~~~~' } =========
Promise { <rejected> 'error' } ++++
Promise { 'fail ~~~~~' } =========
Promise { <rejected> 'error' } ++++
Promise { 'fail ~~~~~' } =========
......
......
  • catch(onRejected)
    • 为当前Promise对象A添加一个拒绝回调函数,返回一个新的Promise对象B
    • 如果A进入fulfilled状态,则A的完成结果作为B的完成结果
    • 如果A进入rejected状态,回调函数执行,回调函数的返回值作为B的完成结果
var myPromise = new Promise(function (resolve, reject) {
    console.log('do sth.')
    setTimeout(() => {
        console.log('~~~~~');
        resolve('ok ~~~~');
    }, 3000);
});

console.log(myPromise, '------------');

setInterval(() => {
    console.log(myPromise, '++++');
}, 1000);

var pro1 = myPromise.then(
    value => {
        console.log('success:', value);
        return 'success ~~~~~';
    },

    reason => {
        console.log('fail:', reason);
        return 'fail ~~~~~';
    }
);

setInterval(() => {
    console.log(pro1, '=========');
}, 1000);

var pro2 = pro1.catch(
    reason => {
        console.log('fail:', reason); // 如果不能catch到,则以当前pro1对象的结果作为pro2的结果
        return 'fail ******'
    }
);

setInterval(() => {
    console.log(pro2, '!!!!!!!!!!!!!!');
}, 1000);
---------------输出结果----------------
do sth.
Promise { <pending> } ------------
Promise { <pending> } ++++
Promise { <pending> } =========
Promise { <pending> } !!!!!!!!!!!!!!
Promise { <pending> } ++++
Promise { <pending> } =========
Promise { <pending> } !!!!!!!!!!!!!!
~~~~~
success: ok ~~~~
Promise { 'ok ~~~~' } ++++
Promise { 'success ~~~~~' } =========
Promise { 'success ~~~~~' } !!!!!!!!!!!!!!
Promise { 'ok ~~~~' } ++++
Promise { 'success ~~~~~' } =========
Promise { 'success ~~~~~' } !!!!!!!!!!!!!!
......
......

Promise.resolve(value)和Promise.reject(reason)

  • Promise.resolve(value) 返回 状态为fulfilled的Promise对象
  • Promise.reject(reason)返回 状态为rejected状态的Promise对象
function runAsync() {
    return new Promise(function (resolve, reject) {
        // 异步操作
        setTimeout(function () {
            console.log('do sth...');
            resolve('ok...');
        }, 3000);
    });
}
// 链式调用
runAsync().then(
    value => {
        console.log(value);
        return Promise.reject(value + '*');
    }
).catch(
    reason => {
        console.log(reason);
        return Promise.resolve(reason + '*');
    }
).then(
    value => {
        console.log(value);
        console.log('Promise END');
    }
)
console.log('==== FIN ====');
----------------输出结果------------
==== FIN ====
do sth...
ok...
ok...*
ok...**
Promise END

参考


  • magedu