JavaScript对象模型

JS对象模型

  • JavaScript 是一种基于原型(Prototype)的面向对象语言,而不是基于类的面向对象语言。
  • C++,Java 有类Class和Instance的概念,类是一类事物的抽象,而实例则是类的实体
  • JS是基于原型的语言,它只有原型对象的概念。原型对象就是一个模板,新的对象从这个模板构建从而获取最初的属性。任何对象在运行时可以动态的增加属性。而且,任何一个对象都可以作为另一个对象的原型,这样后者就可以共享前者的属性

定义类

字面式声明方式

以下是字面值创建对象(直接创建对象,无需通过函数或class关键字)

var obj = {
    x : 1,
    1 : "abc",
    "y" : "abc"
}

for (let s in obj)
    console.log(s, typeof(s));
------------------------------------------
1 string
x string
y string

let a = 1, b = 2
let obj = {a, b}
let obj1 = {a:a, "b":b}

console.log(obj)
console.log(obj1)

let c = "abc"
let d = {
    c:100,          // 注意这个c是'c',不是上面的变量c
    [c]:200         // 如果要用c变量的值做为key,就要把c用中括号括起来
}
console.log(d)
------------------------------------------
{ a: 1, b: 2 }
{ a: 1, b: 2 }
{ c: 100, abc: 200 }

ES6之前–构造器

  • 定义一个函数(构造器函数)对象,函数名首字母大写
  • 使用this定义属性(this就是新创建的实例)
  • 使用new和构造器创建一个新对象

如下,最基本的new实例化一个对象

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

p1 = new Point(1,2)   //一定要加new关键字
console.log(p1)
------------------------------------------
Point { x: 1, y: 2 }
function Point3D(x,y,z){
    Point.call(this,x,y)     //借助于call方法,继承父类的方法和属性
        this.z;
        console.log('Point3D')
}

console.log(Point3D);
p2 = new Point3D(10,22,33);
console.log(p2);
------------------------------------------
Point3D
Point3D { x: 10, y: 22, z: 33 }
  • new 构建一个新的通用对象,new操作符会将新对象的this值传递给Point3D构造函数,函数为这个对象创建z属性.
    new后得到一个对象,使用这个对象的this来调用构造器.

  • 如何执行“基类”的构造器方法呢?

    • 使用Point3D对象的this来执行Point的构造器,所以使用call方法,传入子类的this。
    • 最终,构造完成后,将对象赋给p2。

ES6中的class 类

  • 定义使用class关键字,创建的本质还是函数,是一个特殊的函数.
  • 一个类只能拥有一个名为constructor的构造方法,如果没有显式的定义一个构造方法,则会添加一个默认的constructor方法
  • 继承使用extends关键字
  • 一个构造器可以使用super关键字来调用一个父类的构造函数
  • 没有私有属性

基本创建方法如下


class Point{                        //定义基类
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    show(){
        console.log(this,this.x,this.y)
    }
}

// let p1 = new Point(1,2)
// console.log(p1)
// p1.show()

class Point3D extends Point{           //extends关键字继承
    constructor(x,y,z){                //constructor 构造器
        super(x,y);
        this.z = z;
    }
    show(){                                 //方法重写,子类 的show方法覆盖父类的方法show()
        console.log(this.x,this.y,this.z)
    }
}

let p2 = new Point3D(5,6,7)
console.log(p2)
p2.show()
---------------------------------------------
Point3D { x: 5, y: 6, z: 7 }
5 6 7
方法重写
  • 子类中直接重写父类的方法即可
  • 如果需要使用父类的方法,使用super.method()的方式调用
  • 支持箭头函数重写方法

子类访问父类的方法(属性)有先后顺序


class Point{                   //定义基类
    constructor(x,y){
        this.x = x;
        this.y = y;
        this.show = ()=>console.log('Point2d');       //show是 属性
    }
    // show(){                                        //show是方法
    //      console.log(this.x,this.y)
    //  }
    
}


class Point3D extends Point{
    constructor(x,y,z){
        super(x,y);
        this.z = z;
        this.show = ()=>console.log('Point3d');        //show是属性
    }
    // show(){                                         //show是方法
    //     console.log(this.x,this.y,this.z)
    // }
}

let p2 = new Point3D(5,6,7)
p2.show()
------------------------------
Point3d

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

静态方法

在方法名前加上static,就是静态方法了。
静态方法中的this是类,而不是实例

class Point{
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    static p(){                              //有static关键字就是静态方法了
        console.log(this.x,typeof(this))
    }
}

p1 = new Point(1,2);
console.log(p1,Point)
Point.p()
// p1.p()                //实例不能直接访问静态方法,
p1.constructor.p();      //实例可以通过constructor方法访问静态方法
this的坑
  • 创建this属性,但是this是什么就需要看函数是怎么调用了,
    • 普通函数调用方式,this指向全局对象,全局对象是nodejs的global或者浏览器中的window
    • 对象方法的调用,this指向包含该方法的对象
    • call,apply,bind方法调用时,要看第一参数是谁
var city = {
    name : 'beijin',
    getNmaeFunc : function(){
        console.log(this.name);
        console.log(this)
        return function(){
            console.log(this === global);   
            return this.name;
        };
    }
};
console.log(city.getNmaeFunc()())
----------------------------------------------
beijin
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
true
undefined

分析上面的代码:函数执行时,会开启新的执行上下文环境

  • 第三行打印的true,是 console.log(this == global)执行的结果,说明当前是global,因为调用这个返回的函数 是直接调用的,这就是个普通函数调用,所以this是全局对象
  • 第四行undefined,就是因为this是global,没有name属性。
  • 这就是函数调用的时候,调用方式不同,this对应的对象不同,它已经不是C++、Java的指向实例本身了 。
  • this的问题,这是历史遗留问题,新版只能保留且兼容了。

解决this坑的方法有

显式传入参数
var city = {
    name : 'beijin',
    getNmaeFunc : function(){
        console.log(this.name);
        console.log(this)
        return function(that){
            console.log(that === global);
            console.log(that)
            return that.name;
        };
    }
};
console.log(city.getNmaeFunc()(city))     //显式传入city参数
----------------------------------------------
beijin
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
false
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
beijin
引入apple,call 方法
var city = {
    name : 'beijin',
    getNmaeFunc : function(){
        console.log(this.name);
        console.log(this)
        return function(){
            console.log(this === global);
            console.log(this)
            return this.name;
        };
    }
};
console.log(city.getNmaeFunc().call(city))   //call方法显示传入this对应的对象
----------------------------------------------
beijin
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
false
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
beijin

  • apply、call方法都是函数对象的方法,第一参数都是传入对象引入的。
  • apply传其他参数需要使用数组
  • call传其他参数需要使用可变参数


function Point(x,y){
    this.x = x;
    this.y = y;
    console.log(this === global);
    console.log('Point-------');
}

p1 = new Point(1,2);
console.log(p1)

p2 = new Object();
console.log(p2)

// p3 = Point.call(p2,10,11);
p3 =  Point.apply(p2,[10,20])
console.log(p2);
console.log(p3,typeof(p3));
----------------------------------------------
false
Point-------
Point { x: 1, y: 2 }
{}
false
Point-------
{ x: 10, y: 20 }
undefined 'undefined'
bind方法 (最常用)
var city = {
    name : 'beijin',
    getNmaeFunc : function(){
        console.log(this.name);
        console.log(this)
        return function(){
            console.log(this === global);
            console.log(this)
            return this.name;
        };
    }
};
// console.log(city.getNmaeFunc().bind(city))
var s = city.getNmaeFunc()
var t = s.bind(city);    //bind 后返回新的函数,之后就直接调用即可(即先绑定,后直接调用)
console.log(t)
console.log(t())
----------------------------
beijin
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
[Function: bound ]
false
{ name: 'beijin', getNmaeFunc: [Function: getNmaeFunc] }
beijin
  • apply、call方法,参数不同,调用时传入this
  • bind方法是为函数先绑定this,调用时直接用。
ES6引入支持this的箭头函数

ES6新技术,就不需要兼容this问题了


class city{
    constructor(){
        this.name = 'beijin';
    };
    getNmaeFunc(){
        console.log(this.name);
        console.log(this)
        return ()=>{
            console.log(this === global);
            console.log(this)
            return this.name;
        };
    };
  
};
console.log(new city().getNmaeFunc()())  //ES6直接调用即可
-----------------------------------------------
beijin
city { name: 'beijin' }
false
city { name: 'beijin' }
beijin

高阶类,高阶类或称Minxin模式

Mixin模式,混合模式。
JS是基于对象的,类和对象都是对象模板。
混合mixin,指的是将一个对象的全部或者部分拷贝到另一个对象上去。其实就是属性了。
可以将多个类或对象混合成一个类或对象。

class Point2d {                    //基类
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
}

const ClsMixin = params=>class extends params{        //中间类,Mixin类(实际上是一个函数),为类补属性
    constructor(x,y){
        super(x,y);
        if(typeof(this.check) !== "function"){
            throw new ReferenceError('should define stringify.');
        }
    }
}

class Point3d extends ClsMixin(Point2d){      //继承Mixin类,Mixin传入参数(基类)
    constructor(x,y,z){
        super(x,y)
        this.z = z;
    }
    check(){
        console.log('params check')
    }
}

p = new Point3d(1,2,3)
console.log(p)
p.check()
-----------------------------------------------
Point3d { x: 1, y: 2, z: 3 }
params check

注意:
ClsMixin(Point2d)这一步实际上是一个匿名箭头函数调用,返回了一个新的类型,Point3D继承自这个新的匿名
类型,增强了功能。
以上ClsMixin(Point2d)传入的参数是类,返回值也是一个类,这就是高阶类。

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

const ClsMixin = params=>class extends params{
    constructor(x,y,z){
        super(x,y,z);
    }
    check(){                                //为 Point3d 增加了一个check功能
        console.log("check params")
    }
}

point3d =  ClsMixin(Point3d)              //前面的point3d与后面的point3d不是同一个对象,只是为了统一标识符而已
p =new point3d(1,2,3)
console.log(p)
p.check()
-----------------------------------------------
Point3d { x: 1, y: 2, z: 3 }
check params

由上可知,Mixin可以随意组合为类增加属性

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值