面向对象

面向对象

JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。

当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined

例如,创建Array一个对象

    let arr = [1,2,3]

那么,arr的原型链就是

    arr ----> Array.prototype ----> Object.prototype ----> null

Array.prototype定义了indexOf()shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。

而当我们创建一个函数的时候

    function fun(){
        return 1;
    }

那么,函数fun的原型链就是

    fun ----> Function.prototype ----> Object.prototype ----> null

由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

构造函数

除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:

    function Student(name){
        this.name = name
        this.say = function(){
            console.log('Hello' + ',' + this.name)
        }
    }

看到这个函数,你可能会想,这不就是一个普通的函数吗?
这确实是一个普通函数,如果调用它是得这样的

    function Student(name){
        this.name = name
        this.say = function(){
            console.log('Hello' + ',' + this.name)
        }
        return this
    }
    let xiaoming = Student('小明')
    console.log(xiaoming)//小明
    xiaoming.say()//Hello,小明

普通函数的调用一定要return this,因为此时Student里的this环境变量指向的是window,于是无意间创建了全局变量name,并且返回undefined

而构造函数就是用new字符(关键字)来调用函数Student,并返回一个对象,如下:

    function Student(name){
        this.name = name
        this.say = function(){
            console.log('Hello' + ',' + this.name)
        }
    }
    let xiaohong = new Student('小红')
    console.log(xiaohong)//小红
    xiaohong.say()//Hello,小红

所以,如果不写new,Student就是一个普通函数,如果不return this的话就会抛出undefined;如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就省了return this.

新创建的xiaohong的原型链就是

    xiaohong ----> Student.prototype ----> Object.prototype ----> null

也就是说,xiaohong的原型指向的是函数Student的原型。同理,如果你创建又new出了xiaojun xiaopeng结果也都是一样的

    xiaojun ----> Student.prototype ----> Object.prototype ----> null
    xiaopeng ----> Student.prototype ----> Object.prototype ----> null

new Student()创建的对象还从函数原型上或得了一个constructor的属性,它指向函数Student本身

    xiaohong.portotype.constructor == Student.portotype.constructor //true
    xiaohong.prototype.constructor == Student //true
    Object.getgetPrototypeOf(xiaohong) == Student.prototype //true
    xiaohong instanceof Student //true

Student.prototype指向的就是xiaojun xiaopeng的原型对象,这个对象有个属性constructor指向函数Student本身;函数Student也有自己的一个属性prototype指向xiaojun xiaopeng这些对象的原型,但是xiaojun xiaopeng可没有属性直接指向函数Student本身,不过可以用__proto__这个非标准用法来查看. 所以我们就可以认为xiaojun xiaopeng是继承于Student

new的好处

    xiaoming.name; // '小明'
    xiaohong.name; // '小红'
    xiaoming.say; // function: Student.say()
    xiaohong.say; // function: Student.say()
    xiaoming.say === xiaohong.say; // false

为了我们区别,xiaomingxiaohong有着各自的name
xiaomingxiaohong也有着各自的say()方法,虽然有着同一个方法,函数名称和代码都是相同的,但它们是两个不同的函数
所以,如果我们通过new Student()创建了很多对象,这些对象的say()函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存

要让创建对象共享一个say()函数,根据对象属性的查找原则,我们只需要把say()移动到xiaomingxiaohong对象的原型上就可以,也就是Student.prototype
修改代码如下:

    function Student(name){
        this.name = name
    }
    Student.prototype.say = function() {
        console.log('Hello' + ',' + this.name)
    }

new创建基于原型的JavaScript的对象就是这么简单!

忘记写new怎么办

关于new的重要性上述已经说过了
我们还可以编写一个CreateStudent()函数,在内部封装所有的new,代码如下:

    function Student(data){
        this.name = data.name || '匿名'
        this.age = data.age || 22
    }
    Student.prototype.say = function () {
        console.log('Hello' + ' ' + this.name + '!')
    }
    function CreateStudent(data){
        return new Student(data || {})
    }

这个CreateStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:

    let xiaoming = CreateStudent({
        name:'小明'
    })
    console.log(xiaoming.age)//22
    xiaoming.say()//Hello 小明!

如果创建的对象有很多属性,我们只需要传递需要的某些属性,剩下的属性可以用默认值。由于参数是一个Object,我们无需记忆参数的顺序。如果恰好从JSON拿到了一个对象,就可以直接创建出xiaoming

本文链接:https://www.liaoxuefeng.com/wiki/1022910821149312/1023022043494624

原文地址:https://www.cnblogs.com/kaizhadiyishuai/p/11926658.html