第十一站,面向对象

Javascript之旅——第十一站:原型也不佳精晓?

2015/01/28 · JavaScript
· Javascript,
原型

原文出处:
一线码农的博客   

写到那篇,我的js序列也快接近尾声了,所以那一个种类不会遗留js来贯彻面向对象的中央——原型,有些人说原型倒霉精通,其实嘛,要想系统的知晓原型,最简便易行的方式就是探望经典的书,少看些博客,博客这东西只是博主自己的个体掌握,充其量是些配味的佐料。

一:继承

一旦你熟练C#的话,你早晚会明白,所有的类都是再而三于Object的,那样我就有所Object所兼有的功效了,如下图中自己定义的Person类。

亚洲必赢官网 1

从图中能够见到,在C#中随地可见继承,下一步我要做的就是自定义继承,如下图中本身定义的Student类,让它一连Person.Name属性。

亚洲必赢官网 2

那一个对于玩C#的人的话都是很司通见惯的,那么下一个题目来了,那个真的的面向对象的东西,在js中该怎么玩吗?当然就要用到闻明的prototype属性了。

二:用JS来模仿C#的继承

1.默许继承Object

大家都知晓在js中的所有引用类型也一如既往延续于Object,那样也就具备Object的效应了,但是你有没有考虑过,比如下图中的Person到底是怎么继承了Object的有着属性和措施吗?

亚洲必赢官网 3

观察上图后,你是否很诧异吗?其实原理真的很粗略,用chorme的watch
expressions一看你就清清楚楚了。

亚洲必赢官网 4

第一眼观察不晓得你会不会眼晕?听我逐步解释,从地点的图中简单看到,其实有那般个原型链的关联:

p.__proto__ =Person.prototype

Person.prototype.__proto__ -> new Object()

不知底您看懂了没?其实那里最重点的就是__proto__性能,首先你要明了,每个实例都持有如此个__proto__性能,因为那是中央,比如你要找p.toString()方法,
js引擎会优先在Person
function中找toString()方法,发现没有。。。花擦。。。没辙只好通过p.__proto__特性持续往上寻找,到了Person.prototype,从图中得以见到prototype是一个怀有constructor属性的目的,因为唯有一个属性,所以也没找到tostirng()方法,然后沿着Person.prototype._proto__找到了Object,在这边大家就找到了toString()方法。

2.自定义继承

我们精晓prototype是个卓殊重大的性质,为了仿效C#中Student类继承于Person类,这次我索要做的是让Studnet.prototype=new
Person()就好了。

亚洲必赢官网 5

从图中可以看到student实例已经包含Name属性了,我们前几日早就通晓有一个原型链查找的历程,比如我明天由此student.__proto__找到了new
Person(),然后也看到了new
Person()具有Name属性,我想你现在也精晓,在Person函数中也有一个__proto__性能,它是指向Object的,假诺说我在new
Person()中绝非找到,那么会继续通过Person.__proto__(Student.prototype.proto__)继续往上找,平昔找到顶端停止。

三:详解prototype

  1. prototype到底是什么?

从上一章中自己想你对prototype应该有了宏观掌握,可以看出实际prototype只不过是一个带有constructor属性的Object对象,其中constructor属性是指向当前function的一个指针,代码还原如下:

JavaScript

<script type=”text/javascript”> function Person() { this.Name =
“ctrip”; } Person.prototype = { constructor: Person
//指向Person的constructor }; var p = new Person(); </script>

1
2
3
4
5
6
7
8
9
10
11
<script type="text/javascript">
        function Person() {
            this.Name = "ctrip";
        }
 
        Person.prototype = {
            constructor: Person  //指向Person的constructor
        };
 
        var p = new Person();
    </script>

亚洲必赢官网 6

  1. prototype上边的习性可以被所有实例共享。

本条之所以可以共享,是因为各种实例都有__proto__属性,包涵function的prototype属性也是有__proto__特性的,那是因为prototype本质上也是一个目的的实例,所以js在寻找某个属性是不是存在的时候会由此__proto__属性一贯追踪到object。

亚洲必赢官网 7

  1. 假若function中的属性与prototype属性争持了如何是好?

亚洲必赢官网 8

看到答案后,我想你也相当清楚了,毕竟你已经通晓了原型链的查找,因为js引擎查找进程是先从本函数寻找,若是找到就return,找不到后续通过__proto__往上找,很好了解的。

赞 1 收藏
评论

亚洲必赢官网 9

  写到那篇,我的js种类也快接近尾声了,所以那一个系列不会遗留js来促成面向对象的基本——原型,有些人说原型不佳通晓,其实嘛,要想系统

this 相关问题
问题1: apply、call 、bind有如何效果,什么分别

面向对象的语言都有一个类的概念,通过类能够创建四个拥有同等方法和性质的对象,ES6以前并没有类的定义,在ES6中引入类class.

的了然原型,最省事的办法就是探望经典的书,少看些博客,博客那东西只是博主自己的村办精通,充其量是些配味的调料。

共同点(成效): 都是改变函数this对象的指向,

ES5 面向对象

 

区别
call( this, p1, p1,p3)
call()第二个参数之后的兼具参数都是流传函数的值

创制对象(四种方式简介,其它还有动态原型方式、寄生构造函数形式、稳妥构造函数情势等)

一、工厂方式


function createPerson (Name,Age,Job) {

      var man= new Object();

      man.name= Name;

      man.age= Age;

      man.job= Job;

      man.sayName= function () {

              alert(this.name)

    }

  return  man;

}

var personOne=  createPerson (“Erric”,26,”Engineer”);

var personTwo=  createPerson (“Lori”,26,”teacher”);

优点:化解了三个一般对象的创办问题

缺点: ①  对象识别问题不可以解决(即怎么知道一个对象的花色)

二、构造函数形式

function Person (Name,Age,Job) {

      this.name = Name;

      this.age = Age;

      this.job= Job;

      this.sayName= function () {

              alert(this.name)

      }

}

var personOne=  new Person(“Erric”,26,”Engineer”);

var personTwo=  new Person(“Lori”,26,”teacher”);

注一:
若不选取new操作符直接调用函数,那么其性能和艺术都会被添加到window对象里面(因为在大局意义域调用一个格局时,this总是指向window对象)

如: Person(“Erric”,26,”Enginee”)

        window.sayName()  //  弹出 “Erric”

          window.name            //  “Erric”

          window.age              //  26

注二: new 操作符实际上举行了以下操作

          ① 创造一个新的靶子

          ② 将构造函数的功能域赋给新对象(this指向了这些新的对象)

          ③ 执行构造函数中的代码(为那么些新对象添加属性)

          ④ 重临这么些新的靶子

优点:① 不用显式的成立对象

            ② 将性能和办法赋给了this对象

            ③ 没有return语句

缺点:① 
每个方法都要在每个实例上重新创立两次(personOne和personTwo中的sayName方法不是同一个办法,每个函数都是一个目标,故每 
定义了一个函数就实例化了一个对象)。

           
此题材也可以经过将艺术单独抽出来解决(不过方法一多,都移到全局的话封装性就无从谈起),如下:

            function Person (Name,Age,Job) {

                    this.name = Name;

                      this.age = Age;

                      this.job= Job;

                      this.sayName= sayName

            }

            function sayName() {

                    alert(this.name)

              }

            var personOne=  new Person(“Erric”,26,”Engineer”);

第十一站,面向对象。            var personTwo=  new Person(“Lori”,26,”teacher”);

            ② 如果将公共的sayName方法移到全局,那么又不曾封装性可言了。


三、原型方式

function Person () {

}

Person.prototype.name= “Erric”

Person.prototype.age= “28”

Person.prototype.job= “Job”

Person.prototype.sayName= function () {

        alert(this.sayName)

}

优点:①  缓解了函数共用的问题,不用每个实例都创建四次方法。

缺点:①  不可能传参

            ②
若是实例中修改了原型中的属性(引用类型)或艺术,那么那个特性或格局会被彻底的修改,而影响到其他实例。


四、构造函数+原型组合方式

function Person (Name,Age,Job) {

          this.name= Name

          this.age= Age

          this.job= Job

}

Person.prototype.sayName= function () {

          alert(this.name)

}

//
上面往原型上添加属性和章程的也可正如写,不过此时原型的constructor不指向Person构造函数,而是指向Object,因为Person.prototype就像一个新的靶子实例,它的__proto__指向Object原型。

//  Person.prototype= {

          constructor: Person,            //
重新再实例中定义constructor的对准,覆盖Object原型中的constructor指向

          sayName: function () {

                  alert(this.name)

          }

}

var personOne=  new Person(“Erric”,26,”Engineer”);

var personTwo=  new Person(“Lori”,26,”teacher”);


原型对象的知晓(紧要)

1.率先得知道以下三点:

① 每个函数(含构造函数)都有一个prototype属性,指向Person原型

② 每个实例都有一个__proto__属性,也指向Person原型

③ 每个原型都有一个constructor属性,指向其相应的构造函数

构造函数、实例、原型三者关系如下图:

亚洲必赢官网 10

2.万物皆目标,表达原型链的最开头点都是Object,所以任何一个引用类型的
instanceof Object都会回来true。


一:继承

apply( this, [p1,p2,p3])
apply() 只有五个参数, 首个是obj, 第三个是数组,数组中是该函数的参数

类的后续(二种方法)

一、原型链继承

        对于什么是原型链?

       
每个构造函数都有一个原型对象,原型对象的constructor指向那一个构造函数本身,而实例的__proto__属性又针对原型对象。这么些只要一个实例的__proto__其间指针指向其原型,而它的原型又是另一个项目标实例,那么它的原型又将针对另一个原型,另一个原型也隐含一个对准它的构造函数的指针,若是另一个原型又是另一个类其他实例,那样少见递进,就结成了实例与原型的链子,那就是原型链的基本概念。

落实原型链的继承方式为主如下:

function Father () {

      this.appearance = “beautiful”

}

Father.prototype.sayHappy = function () {

        alert(“快乐”)

}

function Child () {

          this.name= “Jhon”

}

Child.prototype= new Father()        //  继承了父类的法门和属性

Child.prototype.addArr= [1,2,3,4,5]

var child= new Child()
child.sayHappy()          //  弹出“快乐”
child.appearance        //  “beautiful”

child.addArr                      //  [1,2,3,4,5]

原型链继承的弱项:①  不能传参  ②
若原型上的格局时引用类型的话,不小心被改动了的话会影响其他实例。


二、借助构造函数继承(利用calll和apply改变this指针)

基本思路:在子类型构造函数的其中调用超类型的构造函数。

function Father (Hobby){

      this.hobby= Hobby

}

Father.prototype.sayHappy = function () {

      alert(“快乐”)

}

function Child () {

      this.name= “Jhon”

      Father.call(this,”Play Games”)          // 
或者Father.apply(this,[“Play Games”]),继承了Father的习性和方法

}

var child =  new Child()
child.sayHappy                //
从没反应,原型上的方法和总体性不会连续
child.hobby                      //  “Play Games”

借助于构造函数继承的缺点:① 
办法都在构造函数中定义,函数的复用无从谈起    ② 
超类中的方法对子类不可见。


三、组合继承(也叫经典一连,将原型链和看重性构造函数继承相结合)

思路:1.原型链达成对原型属性和艺术的一而再;

            2.构造函数完结对实例属性的继承,且调用基类的构造函数;

function Father(Hobby) {

          this.hobby= Hobby;

          this.exGF = [‘cuihua’, ‘erya’]

}

Father.prototype.sayHappy = function () {

          alert(“快乐”)

}

function Child () {

          this.name= “Jhon”

          Father.call(this,”Play Games”)          // 
或者Father.apply(this,[“Play Games”]),继承了Father的属性和艺术

}

Child.prototype= new Father()

Student.prototype.sayName= function () {

          alert(this.name);

}

var liHua= new Child()

liHua.sayHappy()

liHua.sayName()


检测对象属性的二种艺术:

object.hasOwnProperty(属性名),那么些法子检测的是目的实例的性能(倘使重返true),不可能检测原型上的性质。

in操作符,检测对象拥有的属性,包括原型和实例上的额,有的话就回到true.


判定一个原型是不是在某个实例的原型链上:

Person.prototype.isPropotypeOf(personOne)    //  true

Object.prototype.isPropotypeOf(personOne)      //  true

看清一个构造函数是或不是在实例的原型链中出现过:

personOne instanceof Person                //  true

personOne instanceof Object                //  true


若果你熟谙C#的话,你肯定会了然,所有的类都是再三再四于Object的,那样自己就具有Object所所有的效应了,如下图中自我定义的Person类。

bind() 方法和前两者差距在于: bind()
方法会重回执行上下文被转移的函数而不会即时施行,而前双方是直接实施该函数。他的参数和call()相同。

ES6 面向对象

ES6中引入了Class(类)那一个概念,通过重大字class可以创造一个类。类的数据类型就是函数,类的持有办法都定义在prototype属性上。

class Person () {
        constructor (x,y) {
              this.name= x
              this.age= y
        }
        sayName () {
                alert(“快乐”)
        }
}
var liHua= new Person(“张俊泽”,26)

注:
可以领略为constuctor中的属性和方式为ES5中的构造函数部分,和constructor同级的是ES5中原型上的不二法门和性能。


ES6的继续通过extends关键字贯彻

class Father(){}
class Child extends Father {
        constructor(x,y,color){
                  super(x,y)
                  this.color= color
        }
        toString() {
                retunr “世界和平!”
        }
}

上边代码中,constructor方法和toString方法之中,都冒出了super关键字,它在此地表示父类的构造函数,用来新建父类的this对象。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有和谐的this对象,而是继续父类的this对象,然后对其开展加工。如果不调用super方法,子类就得不到this对象。


类的prototype和__proto__属性

Class作为构造函数的语法唐,同时有prototype和__proto__特性,由此存在两条继承链:

①  子类的__proto__,表示构造函数的持续,总是指向父类

② 
子类的prototype属性的__proto__属性,表示方法的存续,总是指向父类的prototype属性。

class Father {

}

class Child extends Father{

          constructor () {

                  super()

          }

}

var childOne= new Child()

Child.__proto__ ==  Father        //  true

childOne.__proto__ ==  Child.prototype        //  true

Child.prototype.__proto__ ==  Fahter.prototype            //  true

亚洲必赢官网 11

call() apply() 直接t调用实践函数,
bind()
方法会再次来到执行上下文被转移的函数而不及时实施,必须加()才能立刻调用执行
bind() 参数与call() 相同

从图中可以观察,在C#中遍地可见后续,下一步我要做的就是自定义继承,如下图中自己定义的Student类,让它继续Person.Name属性。

题目2: 以下代码输出什么?

亚洲必赢官网 12

var john = { 
  firstName: "John" 
}
function func() { 
  console.log(this.firstName + ": hi!")
 // console.log(this)
}
john.sayHi = func
john.sayHi()   // 输出 John: hi!


/*
john.sayHi= func
sayHi: function func(){}
john.func()
this=== john
john.sayHi()  执行函数
this.firstName='John' 找到自己的属性
*/

这么些对于玩C#的人来说都是很司通见惯的,那么下一个题材来了,那个真正的面向对象的事物,在js中该怎么玩呢?当然就要用到闻明的

题目3: 上面代码输出什么,为啥

prototype属性了。

func()   //输出: Window
function func() { 
  alert(this)  //函数中的this是全局Window对象  因为this 在函数内找不到, 往外找
}

 

题目4:上边代码输出什么

二:用JS来模仿C#的继承

document.addEventListener('click', function(e){
    console.log(this);  // #document
    setTimeout(function(){
        console.log(this);   // Window
    }, 200);
}, false);

1.默许继承Object

问题5:上面代码输出什么,why

 大家都了然在js中的所有引用类型也一样继承于Object,这样也就具备Object的职能了,不过你有没有考虑过,比如下图中的Person到底是怎么继承

var john = { 
  firstName: "John" 
}

function func() { 
  alert( this.firstName )
}
func.call(john)  //输出: John

call(参数)  参数就是要指向的对象this
而 call(john)  john 就是this对象

了Object的保有属性和办法呢?

题材6: 以下代码有哪些问题,怎样修改

亚洲必赢官网 13

var module= {
  bind: function(){
    $btn.on('click', function(){
      console.log(this) //this指 $btn
      this.showMsg();
    })
  },

  showMsg: function(){
    console.log('饥人谷');
  }
}

修改: 因为this=$btn , $btn  没有 showMsg() 方法,
所以不能调用
var module= {
  bind: function(){
    var  _this= this
    $btn.on('click', function(){
      console.log(this) //this指 $btn
      _this.showMsg();
    })
  },

  showMsg: function(){
    console.log('饥人谷');
  }
}

看看上图后,你是否很奇异吗?其实原理真的很简单,用chorme的watch
expressions一看您就清清楚楚了。

原型链相关题材

题目7:有如下代码,解释Person、
prototype、_proto_、p、constructor之间的涉及。

function Person(name){
    this.name = name;
}
Person.prototype.sayName = function(){
    console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();


解释:
p.__proto__=== Person.prototype
Person.prototype.constructor=== Person

 p 是Person 的实例对象, 拥有Person 的属性
p.__proto__指向了Person的prototype对象,及找到其中的 sayName()方法

题材8: 上例中,对目的 p可以这么调用 p.toString()。toString是哪里来的?
画出原型图?并解释怎样是原型链。

1 调用p.toString()

toString()是继承Object原型对象中定义的toString() 方法。
(1) 首先实例p 会先物色自身有没有其一点子。
(2) 若没有, 则通过
p._proto_去找Person.prototype中有没有toString()。
(3) 还一直不找到, 继续往下寻找,
Person.prototype._proto__本着了Object.prototype,
在prototype中找到了toString()

亚洲必赢官网 14

Paste_Image.png

怎样是原型链:
由于_proto_是其他对象都有些属性, 而js 中万物皆是目的,
所以形成一条_proto_连起来的链条,递归访问_proto_务必干净,并且值为null

题材9:对String做扩大,已毕如下方式取得字符串中频率最高的字符

//原型链 一直找到String
String.prototype.getMostofen= function(){
  var dict={};            // 将所有的字符串一次加到 dict,同时为相同值计数
  for(var i=0; i<this.length; i++){
    if(dict[this[i]]){
      ++dict[this[i]]

    }else{
      dict[this[i]]=1
    }
  }


  var count=0,
      maxValue;
  for(key in dict){
    if(dict[key]>count){
      maxValue= key;
      count= dict[key]
    }
    return maxValue;
  }

}


var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因为d 出现了5次

题材10: instanceOf有何功效?内部逻辑是怎么落实的?

看清一个目的是否某个构造函数的实例

p instanceOf Person

查看 p._proto_是否指向Person
若果没有, 继续查找p._proto_._proto_是还是不是指向Person
,没有的话, 继续往下寻找,直到查找到结尾都没有时, 重返false
查找到,返回 true

亚洲必赢官网 15

接轨相关问题

题目11:继承有何样意义?
一个目标足以一向选拔另一个对象中的属性和章程

我对象可以在继续的基础上, 在为祥和添加属性等

题目12: 下边三种写法有哪些界别?

//方法1

function People(name, sex){
    this.name = name;
    this.sex = sex;
    this.printName = function(){
        console.log(this.name);
    }
}
var p1 = new People('饥人谷', 2)

//方法2

function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

Person.prototype.printName = function(){
    console.log(this.name);
}
var p1 = new Person('若愚', 27);

率先种不管怎么,只假如开创新的对象 就需求直接接轨了这么些属性和 方法,
不管用到没有到 ,都会继续到新的靶子中。那样浪费了 内存空间
其次种 是将函数的形式 放在原型对象中, 那样想用就足以行使

问题13: Object.create 有怎么着效果?包容性怎么样?

Obiect.create(参数),
参数为将成立的一个原型对象, 可以利用指定原型对象中的方法和性质

student.prototype=Object.create(Person.prototype)

题材14: hasOwnProperty有何效果? 如何行使?
判断一个对象中是还是不是包涵自定义属性,而不是 原型链上的习性
很特殊, 是js中唯一一个甩卖属性,然则不查找原型链的函数

function Student(){
this.name=name
}
var s= new Student()
s.hasOwnProperty(‘name’) //true
s对象中有没有自己的特性name

题材15:如下代码中call的功效是何等?

function Person(name, sex){
    this.name = name;
    this.sex = sex;
}
function Male(name, sex, age){
    Person.call(this, name, sex);    //这里的 call 有什么作用
    this.age = age;
}

对象属性继承功能:call 直接调用
Person函数,将Person中的属性都拉长到Male函数中了
this是 Male的实例

问题16: 补全代码,达成持续

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

Person.prototype.getName = function(){
  console.log(this.name)
};    

function Male(name, sex, age){
   this.age= age
   Person.call(this, name,sex)    //获取对象属性
}


Male.prototype=Object.create(Person.prototype)  //getName()方法获取
Male.prototype.construtcor=Male

Male.prototype.printName = function(){
    console.log(this.name+'你好')
};
Male.prototype.getAge = function(){
    console.log(this.age+'岁')
};

var ruoyu = new Male('若愚', '男', 27);
ruoyu.getName();
ruoyu.printName();

率先眼观看不了解你会不会眼晕?听我逐步解释,从地点的图中简单看到,其实有那样个原型链的涉及:

p.__proto__ =Person.prototype

Person.prototype.__proto__ -> new Object()

不通晓您看懂了没?其实这里最紧要的就是__proto__性能,首先你要了然,每个实例都装有如此个__proto__属性,因为那是着力,比如您要找

p.toString()方法, js引擎会优先在Person
function中找toString()方法,发现并未。。。花擦。。。没辙只可以经过p.__proto__性能持续往上

招来,到了Person.prototype,从图中可以观望prototype是一个兼有constructor属性的对象,因为只有一个性质,所以也没找到tostirng()方法,

下一场沿着Person.prototype._proto__找到了Object,在此处大家就找到了toString()方法。

 

2.自定义继承

我们清楚prototype是个可怜紧要的性能,为了仿效C#中Student类继承于Person类,这一次自己索要做的是让Studnet.prototype=new
Person()就好了。

亚洲必赢官网 16

从图中得以看到student实例已经包罗Name属性了,大家现在曾经精通有一个原型链查找的经过,比如我今天经过student.__proto__找到了new
Person(),

下一场也观察了new
Person()具有Name属性,我想你现在也清楚,在Person函数中也有一个__proto__属性,它是指向Object的,假如说我在new
Person()

中从不找到,那么会持续透过Person.__proto__(Student.prototype.proto__)继续往上找,一向找到顶端截止。

 

三:详解prototype

  1. prototype到底是如何?

从上一章中我想你对prototype应该有了宏观驾驭,可以看到实际prototype只可是是一个暗含constructor属性的Object对象,其中constructor属性是指

亚洲必赢官网,向当前function的一个指南针,代码还原如下:

 1 <script type="text/javascript">
 2         function Person() {
 3             this.Name = "ctrip";
 4         }
 5 
 6         Person.prototype = {
 7             constructor: Person  //指向Person的constructor
 8         };
 9 
10         var p = new Person();
11     </script>

亚洲必赢官网 17

2.
prototype上面的特性可以被有着实例共享。

 
那些之所以可以共享,是因为每个实例都有__proto__特性,包涵function的prototype属性也是有__proto__性能的,那是因为prototype本质上

也是一个对象的实例,所以js在搜索某个属性是不是存在的时候会透过__proto__特性一贯追踪到object。亚洲必赢官网 18

 

3.
借使function中的属性与prototype属性冲突了怎么做?

亚洲必赢官网 19

见到答案后,我想你也不行精通了,毕竟你早就明白了原型链的检索,因为js引擎查找进度是先从本函数寻找,假设找到就return,找不到持续通过

__proto__往上找,很好掌握的。

网站地图xml地图