浅谈面向对象的javascript几个特征

javascript中的thisnew

javascript是一门很天真的言语,尤其是function。他即能够以面向历程的体式格局来用,比方:

function getName() {
    return '张三'
}
getName()

也能够以面向对象的体式格局来用,比方:

function User() {
    this.name = '张三'
}

var user = new User()

javascript是怎样完成面向对象编程的呢?他供应了new这个关健字,有了new就能够把对象举行实例化,比方:

function User(name, age){
    this.name = name
    this.age = age
}
var zs = new User('zs', 20)
var ls = new User('ls', 30)

new出来的两个实例,会拓荒两块新的内存地区,来保留这些数据,同时有指针指向对象User。所以就有instanceof这个运算符,这个运算符的意义就是:a是否是A的实例。比方上例:zs instanceof User的返回值是true
即然是面向对象的编程言语,那this也是不可或缺的。在javascript中,this永久指向的是他的调用者。要明白这句话,我们举几个例子:

例子1

function test(){
    this.name = 'zs'
}
test()

当实行完成以后,这个name会直接挂载到window下面,由于是如许实行的:winow.test()

例子2

var game = document.getElementById('game')
game.addEventListener('click', function () {
    setTimeout(function () {
        this.innerText = 'Clicked'
    }, 1000)
})

这个例子很简单,点击某个元素的时刻,1秒后,让他内里的html改成Clicked,然则你发明如许不好使,就是由于this指向的题目,由于这内里的this也指向window了,所以你实行window.innerText会返回Clicked

例子3

function User(name) {
    this.name = name
    this.getName = function () {
        console.log(this.name)
    }
}
var u = new User('zs')
u.getName()

这内里的this的指向没有题目,由于根据之前的准绳,调用者是u,也就是User的实例,所以在要领getName中,this.name相当于u.name,所以打印出zs

prototype和__proto__

prototype

javascript是面向对象的言语,这个上面已提过了,其他面向对象言语有一个必备我就是继续,很显然在ES6之前,没有extends这个关键字,那末,javascript就是应用prototype的原型链来完成继续。先记着这句话,我们一会会说到继续。prototype实在只是对象的一个属性,在Chrome掌握台下,能够直接看出来,然则这个属性很特别,这个属性下能够挂载任何的对象、要领、属性,而且挂载的东西能够反应到实例下对象上。说的比较绕,我们看个例子:

function User(name) {
    this.name = name
}
User.prototype.getName = function () {
    console.log(this.name)
}
var u = new User('zs')
u.getName()

我们在User.prototype上面挂载了getName的要领,在下面实例化User以后的u,就能够接见这个要领。
看到这,你能够有个疑问,既然是给实例化对象用的,那下面这类体式格局岂不是更好、更直观?

function User(name) {
    this.name = name
    this.getName = function () {
        console.log(this.name)
    }
}
var u = new User('zs')
u.getName()

假如我们和Java言语举行对应,User相当是Classname相当于属性,getName相当于内里的要领,圆满映照!能够如许有一个题目啊,就是太费内存了。由于每new一个对象,都邑占用一块内存地区,如许User内里要领属性越多,那末每一个实例化的对象都邑对这些举行 深复制,那末占用的内存空间就越大。那末javascript是怎样经由过程prototype来处理内存占用的题目的呢?这就须要援用__proto__

__proto__

定义:__proto__是存在于实例化后对象的一个属性,而且指向原对象的prototype属性
比方上例中的u.__proto__ === User.prototype返回的是true。能够在Chrome掌握台下检察u.__proto__

你会发明,不对吧,User对象下也有__proto__啊。那是由于User也是Function的实例,不信你能够试一下User.__proto__ === Function.prototype的返回值。实在我们如许定义函数:function test(){}是一个语法糖的写法,全拼应该是如许:var test = new Function('alert(1)')

如今我们来诠释为何运用prototype能节约内存。不知道你有无注意到上面一句代码u.__proto__ === User.prototype,我为何要运用三等?由于三等号除了值、范例外,内存地址也必需是相称的,也就是说User不论实例化若干对象,他们的prototype只要一份,放在User里。客户端的浏览器环境不像服务器,内存照样比较慌张的,所以javascript经由过程这类体式格局,来处理内存占用题目。

继续

体式格局一:直接继续

先举个例子:

var Animal = function (name) {
    this.name = name
}
Animal.prototype.walk = function () {
    console.log('I can walk!')
}
Animal.prototype.getName = function () {
    console.log('My name is ' + this.name + '!')
}

var Dog = function (name) {
    this.name = name
}
Dog.prototype = Animal.prototype

var d = new Dog('Tom')
d.getName()
d.walk()

我们竖立一个父类Animal对象,竖立一个子类Dog,我们想让Dog也有walk要领和getName要领,经由过程上面临prototype的相识,我们最早想到的是Dog.prototype = Animal.prototype,如许子类和父类的prototype相称,那子类就有父类一切要领喽,继续链条是如许的:d.__proto__ === Dog.prototype === Animal.prototype
如许很直观,然则也有一个比较严重的题目。我们在扩大Dog的时刻,同时父类也会有对应的要领,这很显然是一个很严重的题目。

体式格局二:实例化继续

为了处理上面的题目,我们须要引入一个空函数,这个空函数做为桥梁,把子类和父类之间的衔接割断。完成以下:

var F = function () {}
F.prototype = Animal.prototype
Dog.prototype = new F()

Dog.prototype.say = function () {
    console.log('Say')
}
  • 为何是Dog.prototype = new F()呢?由于如许即能够继续Animal的一切要领,他的原型链是如许的:
d.__proto__ --> Dog.prototype --> new F().__proto__

实行walk要领,F已有了,所以就不会再找Animal

  • 新增添的要领又不影响父类,这句怎样讲?因实例化的对象没有prototype属性!所以不会影响
    原文作者:会说话的鱼
    原文地址: https://segmentfault.com/a/1190000010398923
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞