JS对象模型

定义类

字面式声明方式

  • 语法
// key(property)不管是什么类型,最后都会强制类型转换为字符串;
// value可以为任意合法的数据类型、变量、表达式
var obj = {
    property:value, // property 可以是一个 标识符
    "property_1":value_1, // 或是一个 字符串
    ["property_2" + 2]:value_2.obj, // 或是一个 可计算的key名
    3:value_3, // 或是一个 数字
    property_4, // 或是一个 变量
} ;
  • 举例
let a = 1;
let b = "xyz";
let c = [1,2,3];
let d = x => x + 1;

var obj = {
    "a":a, // 引号不省略;明确使用该字符串为属性名
    b:b, // 引号省略;强制类型转换为字符串作为属性名
    1:200, // '1': // 数值强制类型转换为字符串作为属性名
    [c.push(5)]:c, // 计算;数组push(5)且返回数组新的长度,强制类型转换为字符串作为属性名
    [c.pop()]:c, // 计算,数组pop(5)且返回5,强制类型转换为字符串作为属性名
    d, // 变量d作为属性名,值为变量d指向的对象
    d:d(3), // 属性重名,后者覆盖前者
    test: (x = 1,2,3,4), // 4,赋值表达式的值x = 逗号表达式的最后一个表达式的值4,
}

console.log(obj)

for (let key in obj){
    console.log(key,typeof key) // key皆为string
}

ES6之前–构造器

  • 语法
    1. 定义一个函数(构造器)对象,函数名首字母大写;
    2. 函数中,使用关键字this定义属性;
    3. 使用关键字new和构造器函数创建一个新对象;
      • new构建一个对象,使用这个this(指向这个对象)调用构造器;

z注意:如果不使用new关键字,就是一次普通的函数调用,函数中的this不指向实例,而是指向global对象。

  • 构造器举例
function Point(x,y){
    this.x = x; // 此处的this,类似于python的类对象中self(指向类实例)
    this.y = y;
    this.show = () => {console.log(this,this.x,this.y)};
    console.log("Point~~~~~~~~~")
    return this.x + this.y // 返回值没用
}

console.log(Point); // [Function: Point],本质是一个函数
var p1 = new Point(4,5); // 必须使用关键字new创建新对象
console.log(p1); // Point { x: 4, y: 5, show: [Function] }
console.log('---'.repeat(15));
  • 继承
  • ES6之前,调用父类的call()方法,实现继承
    • For a given function, creates a bound function that has the same body as the original function;
    • 对于给定的函数,创建一个与此函数具有相同身体的绑定函数;
    • 即父类调用call函数,使用父类的构造器为子类实例创建属性
  • call(this: Function, thisArg: any, …argArray: any[]): any;
    • 参数
      1. this,指向子类对象;注意:会将this的指向对象从global修正为子类对象;
      2. thisAry,传入单个父类构造器函数所需的实参;
      3. …argArray,可以传入多个父类构造器函数所需的实参
    • Father.call(this,…argArray)
      1. 调用父类构造器,为this(此处为子类实例对象)创建属性;
      2. …argArray接收多个实参,传入Father构造器
function Point(x,y){
    this.x = x; // 此处的this,类似于python的类对象中self(指向类实例)
    this.y = y;
    this.show = () => {console.log(this,this.x,this.y)};
    console.log("Point~~~~~~~~~")
}

function Point3D(x,y,z){
    Point.call(this,x,y); // “继承”
    this.z = z;
    console.log("Point3D*********")
}

console.log(Point3D); // [Function: Point3D]

console.log(p2 = new Point3D(6,[3,4]));
console.log('---'.repeat(30))
console.log(p2);
p2.show();

ES6中的class

  • 语法
  1. 类定于使用关键字class。创建的类,本质上还是函数,只不过是一个特殊的函数;

    • 类属性定义:使用关键字this;class中this指向当前类实例;
    • 类方法定义:不能使用关键字function 和 this,直接使用 函数名(){} 的的格式定义;否则抛异常SyntaxError;
  2. 一个类只能拥有一个名为constructor的构造器方法;

    • 如果没有显示的定义一个构造器方法,则会添加一个默认的constructor方法;
    • 使用关键字new创建实例的时候,会调用constructor,参考python类定义中的构造函数__init__;
  3. 继承使用关键字extends;语法为 class 子类 extends 父类;

    • 关键字super
      1. super指向父类类对象;
      2. super(),调用父类构造函数;
      3. super.method(),调用父类show方法;

子类的显示定义了constructor方法,必须调用父类的构造函数,否则抛异常;

注意事项

1. js中只有单继承
1. 子类必须调用父类的constructor方法
1. 必须使用关键字new创建类实例,否则抛异常
  • 举例
class Point {
    constructor(x,y){ // 构造器constructor,注意函数名前没有关键字function和this
        this.x = x; // 关键字this
        this.y = y;
    }

    show(){console.log(this,this.x,this.y)}
}

// let p1 = Point(3,4) // SyntaxError: Identifier 'p1' has already been declared
let p1 = new Point(3,4) // 关键字new
p1.show()

class Point3D extends Point{ // 关键字extends
    constructor(x,y,z){ // 子类有构造器函数,必须调用父类的构造器方法
        super(x,y); // 关键super(),调用父类构造器方法
        this.z = z
    }
}

p3d = new Point3D(3,4,5)
p3d.show() 
  • 重写方法
class Point {
    constructor(x,y){ 
        this.x = x; 
        this.y = y;
    }

    show(){console.log(this,this.x,this.y)}
}

class Point3D extends Point{
    constructor(x,y,z){ 
        super(x,y); 
    }

    show(){ // 方法重写,overide
        super.show(); // super代指类对象
        console.log(this.z)
    }
}

p3d = new Point3D(3,4,5)
p3d.show() // Point3D { x: 3, y: 4, a: 1, z: 5 } 3 4,Point3D类继承父类的show方法

  • 箭头函数
    • 类属性定义,可以使用箭头函数
    • 类方法定义,不能使用箭头函数
  • 继承
    • 父类定义了类属性show,父类和子类调用show,均访问类属性show
    • 父类未定义类属性show,子类优先使用自己的show方法
class Point {
    constructor(x,y){ 
        this.x = x; 
        this.y = y;
        this.show = () => {console.log("Point")} // 注释这行代码,p3d.show()返回Point3D
    }

    show() {
        console.log(this.x,this.y)
    }
}

class Point3D extends Point{
    constructor(x,y,z){ 
        super(x,y); 
        // this.show() => {console.log(5)}  // 放开这行代码,返回5
    }

    show(){
        // super.show()
        console.log("Point3D")
    }
}

p3d = new Point3D(3,4,5)
p3d.show() // 皆返回Point
  • 父类、子类使用同一种方式定义属性或者方法,子类覆盖父类;

注意:访问同名属性或方法时,访问优先级 子类属性 > 父类属性 > 子类方法 > 父类方法


静态属性

  • 在类属性名前加上static,就是静态属性
    • 静态属性目前还没有得到很好的支持

静态属性,就是类的;只能类对象访问,类实例不能访问

class Point {
    static s = 100 // 定义静态方法
    s = 100 // 等价在构造器中,this.s = 100

    // let s = 100  SyntaxError
    // const s = 100  SyntaxError
    // var s = 100  SyntaxError

    constructor(x,y){ 
        let a = 100; // 仅仅在构造器中声明了一个本地局部变量
        const b = 200; // 仅仅在构造器中声明了一个本地常量
        var c = 300; // 仅仅在构造器中声明了一个本地全局常量

        // d = 400; ReferenceError: d is not defined
        this.x = x; // 实例创建属性x
        this.y = y;
        this.show = () => {console.log("Point")}
    }

    show() {
        console.log(this.x,this.y)
    }
}

p1 = new Point(2,3)
console.log(p1.s) // undefined
console.log(Point.s) // 100

静态方法

  • 在类方法名前加上static,就是静态方法

静态方法,就是类的;只能类对象访问,类实例不能访问

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

    // 静态方法中,不能引用this,使用原则同python
    // 1. 实例不存在;
    // 2. 就算实例存在,一个抽象的类对象,如何访问千千万万个实例:
//     static show() {
//         console.log(this.x,this.y) // undefined undefined
//     }   
    static show() {
        console.log("show")
        return  // 等价return undefined
    }
}

p1 = new Point(2,3)
// console.log(p1.show) // p1不能访问静态方法show,即没有此属性,返回undefined
// p1.show() // 等价undefined(),抛TypeError: p1.show is not a function
console.log(Point.show(),'~~~~~~') 
console.log(p1.constructor.show(),'*****')

类对象不能访问类实例对象的属性和方法

class Point {
    constructor(x,y){ 
        this.x = x; 
        this.y = y;
    }
 
    show() {
        console.log("show")
        return  // 等价return undefined
    }
}

p1 = new Point(2,3)
console.log(Point.x) // undefined
console.log(Point.show) // undefined
Point.show() // 等价undefined(),抛TypeError: Point.show is not a function
  • 静态的概念与Python的静态完全不同,相当于Python中的类变量

注意:静态的就是类的,只能类对象访问;实例的就是实例的,只能实例对象访问。


this的坑

坑的来源

  • 虽然js和C++、Java一样有this,但是JS的表现是不同的。

    • C++、Java是静态编译型语言,this编译期绑定;
    • JS是动态语言,this运行期绑定;
  • 函数执行时,会开启新的 执行上下文环境ExecutionContext

    • 函数中创建this属性,函数调用的方式不同,this指向的对象不同

普通函数方式调用,this指向全局对象;

  • nodejs中,全局对象为global
  • 浏览器中,全局对象为window
    function myFn(){
        this.show = () => {console.log(this)}
        return show;
    }

    let f = myFn(1,2);
    f(); // 指向Object [global]

对象方法的调用方式(关键字new创建新对象)/ 字面式声明方式

// 对象方法调用
function myFn(){
    this.show = () => {console.log(this)}
    return this.show
}

let f = new myFn(1,2) ; // new创建新对象
f(); // 指向myFn对象

// 字面式声明
let obj = {
    name : 'Obj',
    show : function (){console.log(this)}
}
console.log(obj)
obj.show() // 指向obj对象
  • 嵌套测试
var school = { // 字面式声明,就式定义一个类objcet
    name : 'JS', // 等价this.name = 'JS'
    getNameFunc : function (){ 
        console.log(this.name); // 此处的this指向school对象
        console.log(this);
        return function (){ // 临时生成一个函数对象
            console.log(this == global); // 普通函数调用,this指向全局对象global
            console.log(this);
            return this.name;
        }
    }
}

console.log(school.getNameFunc()) 
console.log('~~~~~~~~~~~~~~~')
console.log(school.getNameFunc()())

// 运行结果
// JS
// { name: 'JS', getNameFunc: [Function: getNameFunc] }
// [Function]
// ~~~~~~~~~~~~~~~
// JS
// { name: 'JS', getNameFunc: [Function: getNameFunc] }
// true
// Object [global] {
//   global: [Circular],
//   clearInterval: [Function: clearInterval],
//   clearTimeout: [Function: clearTimeout],
//   setInterval: [Function: setInterval],
//   setTimeout: [Function: setTimeout] { [Symbol(util.promisify.custom)]: [Function] },
//   queueMicrotask: [Function: queueMicrotask],
//   clearImmediate: [Function: clearImmediate],
//   setImmediate: [Function: setImmediate] {
//     [Symbol(util.promisify.custom)]: [Function]
//   }
// }
// undefined 

this坑的解决

1. 显示传入对象

2. ES3(ES-262第三版)引入call、apply方法

  • apply、call方法都是函数对象的方法;注意:是函数对象方法

    1. call(this: Function, thisArg, …argArray): any;
      • 第一个参数传入对象引用,修正this的指向对象
      • 其他参数,使用可变参数收集
    2. apply(this: Function, thisArg, argArray): any;
      • 第一个参数传入对象引用,修正this的指向对象
      • 其他参数,需要使用数组
  • 举例

var school = { 
    name : 'JS', 
    getNameFunc : function (){ 
        return function (x,y){ // 临时生成一个函数对象
            return x+y
        }
    }
}

console.log(school.getNameFunc().call(school,3,4)) // 可变参数收集
console.log(school.getNameFunc().apply(school,[3,4])) // 数组
  • 应用
function Point(x,y){
    this.x = x;
    this.y = y;
    console.log(this == global);
    console.log("Point ~~~~~");
    return 100
}

var p1 = Point(4,5);
console.log(p1);
console.log('~~~'.repeat(15));

var p2 = new Object(); 
var p3 = Point.call(p2,10,10); // this指向了p2,调用Point函数,动态为p2对象注入属性x,y;而p3拿到函数的返回值

console.log(p3); // 100
console.log(p2); // { x: 10, y: 10 }

3. ES5引入bind方法 – 最常用

  • bind方法,先为函数绑定this,生成新的函数对象,然后调用时直接用
    • // 类似于Python中的偏函数partial
var school = { 
    name : 'JS', 
    getNameFunc : function (){ 
        console.log(this.name); 
        console.log(this);
        return function (x,y){ 
            console.log(this == global); 
            console.log(this);
            return x+y
        }
    }
}

console.log(school.getNameFunc().bind(school)) // 生成一个新函数对象
console.log(school.getNameFunc().bind(school)(5,5)) // 需要调用

ES6引入支持this的箭头函数

  • ES6新技术,不需要兼容this问题
  • ES6之前的定义方法
var school = { 
    name : 'JS', 
    getNameFunc : function (){ 
        console.log(this.name); 
        console.log(this);
        return (x,y) => { // 箭头函数
            console.log(this == global); 
            console.log(this);
            return x+y
        }
    }
}

console.log(school.getNameFunc()(3,4)
  • ES6 新的定义方法
class school { 
    constructor (){
        this.name = 'JS'
    }

    getNameFunc (){ 
        console.log(this.name); 
        console.log(this);
        return (x,y) => { // 箭头函数
            console.log(this == global); 
            console.log(this);
            return x+y
        }
    }
}

console.log(new school().getNameFunc()(3,4))
  • ES6 新的定义方法
class school { 
    constructor (){
        this.name = 'JS'
    }

    getNameFunc (){ 
        console.log(this.name); 
        console.log(this);
        return (x,y) => { // 箭头函数
            console.log(this == global); 
            console.log(this);
            return x+y
        }
    }
}

console.log(new school().getNameFunc()(3,4))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值