从无到有的学习JavaScript之对象模型——第五篇

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

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

1.定义类

1.1 字面声明方式

let obj = {
    a:1,
    b: 'abc',
    'c': 100,
    show: () => {
        console.log('show function')
    }

}


let obj1 = {obj}
console.log(obj1)
// { obj: { a: 1, b: 'abc', c: 100 } }

let x = 100, y = 200, z = 'abc'
let obj2 = {x: x, y, z}  // 注意第一个x是字符串key,第二个x是变量,y,z都是变量

// { x: 100, y: 200, z: 'abc' }
console.log(obj2)
let obj3 = {x: x, y, [z]: z}  // 想要使用z变量的值作为key,需要加中括号
console.log(obj3)
// { x: 100, y: 200, abc: 'abc' }

注意:对象的key只能是字符串类型,最后都会被转换成字符串。

2. ES6之前的构造器

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

console.log(Point)  // 只有[Function: Point]

p1 = new Point(4, 5)  // 实例化,如果前面没有new,则只是普通的函数调用,那么打印出的undefined;加new后,后面的就是构造器
console.log(p1)
p1.show()
/*
Point~~~~~~~~~~~
Point { x: 4, y: 5, show: [Function] }
*/
console.log('------------------')

function Point3D(x, y, z){
    Point.call(this, x, y)  // 继承,此时的this是Point3D的实例
    this.z = z
    console.log('Point3D~~~~~~~~~~~~~')
}

console.log(Point3D)
p2 = new Point3D(10, 11, 12)
console.log(p2)
p2.show()
/*
[Function: Point3D]
Point~~~~~~~~~~~
Point3D~~~~~~~~~~~~~
Point3D { x: 10, y: 11, show: [Function], z: 12 }
Point3D { x: 10, y: 11, show: [Function], z: 12 } 10 11
*/

new构建一个新的通用对象,new操作符将新的对象的this值传递给Point3D构造器函数,函数为这个对象创建类z属性。new后得到一个对象,使用这个对象的this来调用构造器,那么如何执行“基类”的构造器方法呢?使用point3D对象的this来执行Point对象的构造器,所以使用call方法,传入子类的this。

3. ES6中的构造器

从ES6开始,新提供了class关键字,使得创建对象更加简单、清晰。

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

let p3 = new Point(1, 2)  // class 定义的是新语法,必须使用new关键字实例化
p3.show()
// Point { x: 1, y: 2 } 1 2

class Point3D extends Point{
    constructor(x, y, z){
        super(x, y)
        this.z = z
        this.show = () => {console.log("Point3D")}
    }
    show(){
        console.log(this, this.x, this.y, this.z)  // 重写了子类的方法,会覆盖父类的方法
    }
}

let p4 = new Point3D(3, 4, 5)
p4.show()
// Point3D { x: 3, y: 4, z: 5 } 3 4

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

class Add{
    constructor(x, y){
        this.x = x
        this.y = y
    }
    static print(){  // 把此处的静态方法理解为python中的classmethod
        console.log(this, this.x)  // this为[Function: Add],不是实例,就是说this指向类,不是类的实例
    }
}

add = new Add(10, 20)
console.log(Add)  // [Function: Add]
Add.print()  // 结果为[Function: Add] undefined =>因为类没有定义x属性
// add.print()  // 注意这里与python不同,实例不能直接访问静态方法,与c++、java是一致的
add.constructor.print()  // 实例访问静态方法,需要借助构造器方法constructor

JS中静态的概念与pyhton的静态方法不同,相当于python中的类方法(classmethod)

4.this的坑

虽然js和c++_、java一样有this,但是js的表现是不同的。原因在于,c++、java是静态编译型语言,this是编译期绑定,而js是动态语言,运行期绑定。

var school = {
    name : 'magedu',
    getNameFunc: function(){
        console.log(this.name)
        console.log(this)
        return function(){
            console.log(this === global)  // this是否是global对象
            return this.name
        }
    }
}

console.log(school.getNameFunc()())
/*
magedu
{ name: 'magedu', getNameFunc: [Function: getNameFunc] }
true // 此处是true说明this是全局变量,是global,没有name属性,所以最后打印undefined
undefined
*/
扫描二维码关注公众号,回复: 6783905 查看本文章

为了分析上面的程序,先学习一些知识:函数执行时,会开启新的上下文环境ExecutionContext。创建this属性,但是this是什么就要看函数怎么调用了。

  • myFunction(1, 2, 3),普通的函数调用方式,this指向全局对象,全局对象是nodejs的global或者浏览器的window。
  • myObject.myFunction(1, 2, 3),对象方法的调用方式,this指向包含该方法的对象。
  • call、apply、bind方法调用时,要看第一参数是谁。

我们在使用时,有时候需要明确的让this必须是我们期望的对象,该如何做呢?解决上述的方法有很多:

显示传入:

var school = {
    name : 'magedu',
    getNameFunc: function(){
        console.log(this.name)
        console.log(this)
        return function(that){
            console.log(this == global)  // this是否是global对象
            return that.name
        }
    }
}

console.log(school.getNameFunc()(school))
/*
**magedu
**{ name: 'magedu', getNameFunc: [Function: getNameFunc] }
**true  
**magedu
*/

通过主动传入对象,避开了this的问题。

ES3引入了apply、call方法:

function Print(){
    this.print = function(x, y) {console.log(x + y)}
}

p = new Print(100, 200)
p.print(10, 20)
p.print.call(p, 10, 20)  // 30
p.print.apply(p, [10, 20])  // 30
// apply、call方法都是函数对象的方法,第一参数都是对象的实例,apply传其他参数需要使用数组

/*
magedu
{ name: 'magedu', getNameFunc: [Function: getNameFunc] }
false  
*/


var school = {
    name : 'magedu',
    getNameFunc: function(){
        console.log(this.name)
        console.log(this)
        return () =>{
            console.log(this == global)  // this是否是global对象
            return this.name
        }
    }
}

console.log(school.getNameFunc().call(school))

/*
magedu
school { name: 'magedu' } 'object'
false
magedu
*/


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

ES5引入bind方法:

var school = {
    name: 'magedu',
    getNameFunc: function(){
        console.log(this.name)
        console.log(this)
        return function (){
            console.log(this === global)
            return this.name
        }
    }
}

var func = school.getNameFunc()
console.log(func)
/*
magedu
{ name: 'magedu', getNameFunc: [Function: getNameFunc] }
[Function]
*/
var new_func = func.bind(school)  // 绑定后会返回新的函数
console.log(new_func)  // [Function: bound ]
console.log(new_func())
/*
false
magedu
*/

apply、call方法,参数不同,调用时传入this。bind方法是为函数先绑定this,调用时直接用。

ES6引入支持this的箭头函数:


// ES6新的定义
class school{
    constructor(){
        this.name = 'magedu'
    }
    getNameFunc(){
        console.log(this.name)
        console.log(this, typeof this)  // this是object
        return () => {
            console.log(this === global)  // this不是全局变量
            return this.name
        }
    }
}

console.log(new school().getNameFunc()())
/*
magedu
school { name: 'magedu' } 'object'
false
magedu
*/

猜你喜欢

转载自blog.csdn.net/sqsltr/article/details/95229552
今日推荐