>

Javascript构造函数和原型

- 编辑:金沙国际平台登录 -

Javascript构造函数和原型

一、轻松类和无参方法

  class Counter{    private var value = 0    def increment(){value += 1} //改变对象状态的方法 使用()    def current = value //取值,不改变对象状态的方法, 不使用()  }  val c  = new Counter  c.increment()  println(c.current)  //直接获取值,不带()

结果

1

改值函数使用()

取值函数不应用()

措施暗中认可是共有的

原文:http://tobyho.com/2010/11/22/javascript-constructors-and/

二、带getter和setter属性

//  class Persion{//    var age = 0 //非private,可以使用getter和setter
//    def current = age //如果age为private,可以自定义一个getter方法//  }  class Persion{    private var privateAge = 0    def age = privateAge          //getter 方法 age    def age_=(newAge:Int) {       //setter 方法 age_=  _和=不能分开,中间不能有空格,       if(newAge>privateAge) privateAge = newAge    }  }  val f = new Persion  f.age = 30  f.age = 20  println

结果

30

图片 1

深信您已经理解了,Javascript函数也能够看作指标构造器。举个例子,为了参考面向对象编制程序中的Class,能够用如下的代码

三、对象私有字段

function Person(name){
    this.name = name
}

四、Bean属性

图片 2

  import scala.beans.BeanProperty  class Persion{    @BeanProperty var age = 0  }  val p = new Persion  p.setAge(10)  println

结果

10

*留意:我不利用分号因为本人是个异信众! *
不管怎么说,你现在有了一个function,你能够利用new操作符来成立多个Person

五、协理构造器

  class Persion{    private var age = 0    private var name = ""    def this(name:String){   //辅助构造器      this()         //调用主构造器      this.name = name    }    def this(name:String,age:Int){ //辅助构造器      this     //调用辅助构造器      this.age = age    }      }  val p = new Persion          //主构造器  val p1 = new Persion    //辅助构造器  val p2 = new Persion //辅助构造器
var bob = new Person('Bob')
// {name: 'Bob'}

六、主构造器

  class Persion(val name:String, val age:Int){ //主构造器的参数直接放置在类名之后    println //主构造器会执行类定义中的所有语句  }  val p = new Persion

图片 3

为了确认bob实在是四个Person,能够那样做

七、嵌套类

图片 4

图片 5

1.

    class Counter{      var value = 0L      def increment() = value+=1      def current = value    }    val c = new Counter    c.value=Int.MaxValue    c.increment()    println(c.current)

2.

    class BackAccount{      private var value = 0D      def deposit(money:Double): Unit ={        value += money      }      def withdraw(money:Double): Unit ={        value -= money      }      def current = value    }    val acc = new BackAccount    acc.deposit(100)    acc.withdraw(20)    println(acc.current)

3.

    class Time(val hours:Int,val minutes:Int){      def before(other:Time): Boolean ={        if (hours==other.hours) minutes<other.minutes else hours<other.hours      }    }    val t1 = new Time(2,17)    val t2 = new Time(3,18)    println(t1.before

4.

    class Time(val hours:Int,val minutes:Int){      def before(other:Time): Boolean ={        if (hours==other.hours) minutes<other.minutes else hours<other.hours      }      def display(): Unit ={        println(hours+":"+minutes)      }      def display1(): Unit ={        println(hours*60+minutes)      }    }    val t1 = new Time(2,17)    val t2 = new Time(3,18)    t1.display()    t1.display1()    println(t1.before    println

5.

    import scala.reflect.BeanProperty    class Student{      @BeanProperty val name:String =""      @BeanProperty val id:Long = 0L    }

D:>scalac Student.scalaD:>javap -private StudentCompiled from "Student.scala"public class Student {  private final java.lang.String name;  private final long id;  public java.lang.String name();  public long id();  public java.lang.String getName();  public long getId();  public Student();}

6&7. val

    class Person(val fullname:String){      val firstName:String = fullname.split(0)      val lastName:String = fullname.split(1)      private var privateAge = 0      def age = privateAge      def age_=(newAge:Int): Unit ={        if (newAge<0) privateAge = 0      }    }    val p = new Person("Fred Smith")    println(p.firstName)    println(p.lastName)

8.必填参数设置为主构造器

9.

10.

参考《快学Scala》

bob instanceof Person
// true

你同一能够把Person作为贰个平凡函数调用——不应用new

Person('Bob')
// undefined

可是此间会回来undefined.同期,你在不经意间创制了叁个全局变量name,那可不是你想要的。

name
// 'Bob'

嗯...这或多或少也不佳,特别是若是你早就有二个名称叫name的全局变量,那么它将会被隐敝。这是因为你直接调用了三个函数(不适用new),this指标棉被服装置为全局对象——在浏览器中,正是window对象。

window.name
// 'Bob'
this === window
// true

故此...假如您想写三个布局器函数,那么就用构造器的主意使用它(使用new),尽管您想写一个平常函数,那么就以函数的不二等秘书籍利用它(直接调用),不要相互混淆。

注:一个较好的代码习贯正是,构造器函数首字母大写,普通函数首字母小写。如function Person(){}是多个构造器函数,function showMsg(){}是三个普普通通函数。

某个人或然会建议,能够动用叁个小技能防止污染全局变量。

function Person(name){
    if (!(this instanceof Person))
        return new Person(name)
    this.name = name
}

这段代码做了三件事

  1. 检查this目的是还是不是是Person的实例——假如接纳new操作符的话正是。
  2. 万一它实在是Person的实例,实践原有的代码。
  3. 只要它不是Person的实例,使用new操作符创设一个Person的实例——那才是不容争辩的应用姿势,然后回来它。

那就允许使用函数方式调用构造器函数,重返贰个Person指标,不会污染全局命名空间。

Person('Bob')
// {name: 'Bob'}
name
// undefined

难以置信的是选择new操作符同样有效

new Person('Bob')
// {name: 'Bob'}

缘何吧?那是因为当您使用new操作符创造贰个对象时,倘若您在构造函数里面主动回来一个指标,那么new表明式的值就是以此重回的靶子;若无积极重返,那么构造函数会暗中认可重返this。然则,你可能会想,笔者可不得以重回三个非Person对象呢?那就有一点像棍骗了~

function Cat(name){
    this.name = name
}
function Person(name){
    return new Cat(name)
}
var bob = new Person('Bob')
bob instanceof Person
// false
bob instanceof Cat
// true

由此,笔者成立二个Person结果本身获得了贰个Cat?好呢,在Javascript中那真的大概暴发。你居然能够回到二个Array

function Person(name){
    return [name]
}
new Person('Bob')
// ['Bob']

然而那有一个限制,假若你回去贰个原始数据类型,再次来到值将不起功效。

function Person(name){
    this.name = name
    return 5
}
new Person('Bob')
// {name: 'Bob'}

Number,String,Boolean,都以原本数据类型。
若果你在组织器函数里面重回那一个项目标值,那么它将会被忽视,构造器将如约常规情形,再次回到this对象。

注:原始数据类型还含有undefinednull。但假若您利用new操作符创造原始数据类型,它将会是一个对象

typeof (new String('hello')) === 'object' // true
typeof (String('hello')) === 'string' // true

方法

在最开首的时候我,小编说过函数也足以看作构造器,事实上,它更像身兼三职。函数一样能够看做方法
假诺你精晓面向对象编制程序的话,你会知晓方法是指标的表现——描述对象足以做怎么着。在Javascript中,方法正是链接到目的上的函数——你能够经过成立一个函数并把它赋值到对象上,来创立对象的法子。

function Person(name){
    this.name = name
    this.sayHi = function(){
        return 'Hi, I am ' + this.name
    }
}

鲍伯以往能够say Hi了!

var bob = new Person('Bob')
bob.sayHi()
// 'Hi, I am Bob'

实际上,大家能够脱离构造函数,创设对象的措施

var bob = {name: 'Bob'} // this is a Javascript object!
bob.sayHi = function(){
    return 'Hi, I am ' + this.name
}

这一样有效。只怕,尽管你喜欢的话,把它写成多少个更加大的object

var bob = {
    name: 'Bob',
    sayHi: function(){
        return 'Hi, I am ' + this.name
    }
}

故此,大家怎么还亟需构造函数呢?答案是承接。

原型和后续

好呢,我们谈谈承袭。你一定晓得承继,对吗?譬喻在Java中,你能够让八个类承继另八个类,就足以自行得到有着父类的点子和变量了。

public class Mammal{
    public void breathe(){
        // do some breathing
    }
}
public class Cat extends Mammal{
    // now cat too can breathe!
}

那么,在Javascript中,我们得以做同样的作业,只是稍微分歧。首先,大家竟然未曾类!替代它的是prototype。上边就是与Java代码等价的Javascript代码。

function Mammal(){
}
Mammal.prototype.breathe = function(){
    // do some breathing
}
function Cat(){
}
Cat.prototype = new Mammal()
Cat.prototype.constructor = Cat
// now cat too can breathe!

Javascript分化于古板的面向对象语言,它利用原型承袭。一言以蔽之,原型承接的劳作规律如下:

  1. 贰个指标有非常多特性,包括普通属性和函数。
  2. 一个对象有一个非正规的父属性,它也被称呼那几个目的的原型,用__proto__意味着。那个目的足以一而再它父对象的有着属性。
  3. 贰个对象可以通过在自个儿设置属性,重写父对象的的同名属性
  4. 构造器用于成立对象。每三个构造器都有三个相关联的prototype对象,它实际也是二个普普通通对象。
  5. 开创三个指标时,该指标的父对象(__proto__)棉被服装置为成立它的构造器的prototype对象。

好的!未来您应有清楚原型承袭是怎么贰回事了,接下去大家一行一行看Cat其一例子

第一,大家创造了叁个构造器Mammal

function Mammal(){
}

这时候,Mammal业已有了多少个prototype属性

Mammal.prototype
// {}

咱俩成立一个实例

var mammal = new Mammal()

今日,大家作证一下方面提到的第2条

mammal.__proto__ === Mammal.prototype
// true

接下来,我们在Mammalprototype质量上平添二个主意breathe

Mammal.prototype.breathe = function(){
    // do some breathing
}

这时候,实例mammal就可以调用breathe了

mammal.breathe()

因为它从Mammal.prototype一连过来。往下

function Cat(){
}
Cat.prototype = new Mammal()

我们创建了二个Cat构造器,设置Cat.prototypeMammal的实例。为什么要这样做吧?

var garfield = new Cat()
garfield.breathe()

当今抱有的cat实例都连续自Mammal,所以它也能够调用breathe方法,往下

Cat.prototype.constructor = Cat

确保cat确实是Cat的实例

garfield.__proto__ === Cat.prototype
// true
Cat.prototype.constructor === Cat
// true
garfield instanceof Cat
// true

每当你创设二个Cat的实例,你就能够创造二个二级原型链,即garfieldCat.prototype的子对象,而Cat.prototypeMammal的实例,所以也是Mammal.prototype的子对象。

那么,Mammal.prototype的父对象是什么人吗?没有错,你大概猜到了,这就是Object.prototype。所以,实际上是三级原型链。

garfield -> Cat.prototype -> Mammal.prototype -> Object.prototype

您能够在garfield的父对象上加码质量,然后garfield就足以奇妙的访问到这一个属性,尽管在garfield对象成立之后!

Cat.prototype.isCat = true
Mammal.prototype.isMammal = true
Object.prototype.isObject = true
garfield.isCat // true
garfield.isMammal // true
garfield.isObject // true

你也足以领略它是不是有某些属性

'isMammal' in garfield
// true

何况你也能够区分本人的属性和一而再而来的性能

garfield.name = 'Garfield'
garfield.hasOwnProperty('name')
// true
garfield.hasOwnProperty('breathe')
// false

在原型上创设方法

前天您应有驾驭了原型承接的规律,让我们回来第4个例证

function Person(name){
    this.name = name
    this.sayHi = function(){
        return 'Hi, I am ' + this.name
    }
}

一向在对象上定义方法是一种低功用的方法。一个更加好的方法是在Person.prototype上定义方法。

function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return 'Hi, I am ' + this.name
}

何以这种艺术越来越好?

在第一种方法中,每当大家创建四个person对象,叁个新的sayHi方式就要被成立,而在其次种艺术中,唯有二个sayHi主意被创建了,何况在具有Person的实例中国共产党享——那是因为Person.prototype是它们的父对象。所以,在prototype上创设方法会更为赶快。

Apply & Call

正如你所见,函数依靠增添到对象上而形成了三个对象的法门,那么那个函数内的this指南针应该平素指向这几个指标,不是么?事实而不是这么。大家看看在此之前的例证。

function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return 'Hi, I am ' + this.name
}

你创设多少个Person对象,jackjill

var jack = new Person('Jack')
var jill = new Person('Jill')
jack.sayHi()
// 'Hi, I am Jack'
jill.sayHi()
// 'Hi, I am Jill'

在这里,sayHi方法不是加多在jack或者jill对象上的,而是加多在他们的原型对象上:Person.prototype。那么,sayHi情势如何通晓jackjill的名字吧?

答案:this指南针未有绑定到任何对象上,直到函数被调用时才实行绑定。

当您调用jack.sayHi()时,sayHithis指南针就能够绑定到jack上;当您调用jill.sayHi()是,它则会绑定到jill上。但是,绑定this目的不更动方法本人——它依然同样的四个函数!

您同样可感到一个方式钦定所要绑定的this指针的对象。

function sing(){
    return this.name + ' sings!'
}
sing.apply(jack)
// 'Jack sings!'

apply主意属于Function.prototype(没有错,函数也是多少个对象何况有prototypes和本人的习性!)。所以,你能够在任何函数中选拔apply格局绑定this指南针为钦点的靶子,固然这么些函数未有增加到那几个目的上。事实上,你居然能够绑定this指南针为分歧的对象。

function Flower(name){
    this.name = name
}
var tulip = new Flower('Tulip')
jack.sayHi.apply(tulip)
// 'Hi, I am Tulip'

你恐怕会说

等等,紫述香怎会说话啊!

本身得以应对你

任哪个人是其他事,任何事是任哪个人,颤抖吧人类@_@

如果这么些目的有一个name属性,sayHi措施就能很乐于把它打字与印刷出。那正是鸭子类型准则

若是三个事物像鸭子同样嘎嘎叫,而且它走起来像鸭子一样,对小编的话它就是鸭子!

那么回到apply函数:尽管你想采用apply传送参数,你可以把它们组织成三个数组作为第三个参数。

function singTo(other){
    return this.name + ' sings for ' + other.name
}
singTo.apply(jack, [jill])
// 'Jack sings for Jill'

Function.prototype也有call函数,它和apply函数极度相像,独一的分化正是call函数依次把参数列在结尾传递,而apply函数接收四个数组作为第一个参数。

sing.call(jack, jill)
// 'Jack sings for Jill'

new方法

明日,逸事情来了。

当您想调用七个有若干个参数的函数时,apply艺术丰硕的惠及。例如,Math.max办法接受多少个number参数

Math.max(4, 1, 8, 9, 2)
// 9

那很好,不过相当不足抽象。大家能够使用apply获得到率性数组的最大值。

Math.max.apply(Math, myarray)

那有用多了!

既然apply那般有用,你也许会在不知凡几地点想使用它,比起

Math.max.apply(Math, args)

你恐怕更想在布局器函数中采纳

new Person.apply(Person, args)

可惜的是,那不起成效。它会认为你把Person.apply完全当做了构造函数。那么那样呢?

(new Person).apply(Person, args)

那无差距于也不起成效,因为她会首先创造贰个person对象,然后在尝试调用apply方法。

如何做呢?StackOverflow上的那个答复是个好主意

大家得以在Function.prototype上成立叁个new方法

Function.prototype.new = function(){
    var args = arguments
    var constructor = this
    function Fake(){
         constructor.apply(this, args)
    }
    Fake.prototype = constructor.prototype
    return new Fake
}

那样,全数的构造器函数都有三个new方法

var bob = Person.new('Bob')

咱俩剖析一下new格局的原理

首先

var args = arguments
var constructor = this
function Fake(){
     constructor.apply(this, args)
}

笔者们创立了一个Fake构造器,在constructor上调用apply方法。在new主意的上下文中,this目的指的就是实在的结构器函数——我们把它保存在constructor变量中,一样的,我们也把new艺术上下文的arguments保存在args变量中,以便在Fake构造器中利用。往下

Fake.prototype = constructor.prototype

我们设置Fake.prototype为本来的构造器的prototype。因为constructor针对的依然原本的构造函数,他的prototype性能照旧原先的。所以经过Fake始建的靶子照旧原来的组织器函数的实例。最终

return new Fake

使用Fake构造器成立贰个新指标并重回。

略知一二了么?第贰次不了然不要紧,多看两遍就能够理解了!

总的说来,以后咱们得以干一些很酷的事务了。

var children = [new Person('Ben'), new Person('Dan')]
var args = ['Bob'].concat(children)
var bob = Person.new.apply(Person, args)

很好!为了不写一遍Person,大家得以加上五个相助方法

Function.prototype.applyNew = function(){
     return this.new.apply(this, arguments)
}

近些日子你能够这么使用

var bob = Person.applyNew(args)

那就显得了Javascript是一门灵活的语言。尽管它有些使用方法不是您想要的,你也能够效仿去做。

总结

那篇文章到此地就终止了,大家上学了

  1. Constructors构造器
  2. Methods and Prototypes方法和原型
  3. apply & call
  4. 兑现一个new方法

本文由编程发布,转载请注明来源:Javascript构造函数和原型