关于《Head First JavaScript》的读后感及原型相关概念的理解

/ 0评 / 1

首先来谈一谈读后感

这本书大约有650多页,刨去其中的练习及其答案页,也应该有500多页,所以你可以想想里面有着多少关于JavaScript的知识。有人可能看到这么厚的书,就已经退避三舍、望而却步了。其实,不用害怕,如果你拿这本书来入门JavaScript,我觉得非常好。其实按道理来说,不管哪本书,你只要认真耐着性子去看完读完,相信都会有一番收获的。当然关于这本书,最让我觉得异于其他编程语言书的是,里面充斥着有趣的图画以及作者幽默风趣的描述。给我的感觉是,当遇到一个比较难理解的概念时,作者不会像那些古板的人一样,直接抛出概念让你自己去理解,而是以朋友的角度来和你面对面说一样。因而,我读完这本书,里面的任何概念我都能够理清,并且在其他任何代码中看到它时,我都能认出它。所以,这本书总的来说就是风趣幽默的同时又给你认真地讲解概念,(也可能是由于国外作者的缘故,让这本书形成了这种叙述风格),好书当然得重读一遍,不过价格确实有点小贵,单本129元,不过我当时在某东遇上活动买了一堆书,就显得这本书单价就很便宜了,值得推荐。


对象模型

由于我之前学过Java,就在学JavaScript之前了解过一点对象的概念,但这并不妨碍我去理解JS中的对象,反而是一种帮助。学过Java的都知道,创建对象需要通过类,但JavaScript根本就没有类,而是通过从其他对象那里继承行为和属性,这被称为原型式继承,也叫基于原型的继承。


在没有学习过原型继承之前,我们都是通过对象字面量来创建一个对象,比如:

var fido = {
      name: "Fido",
      breed: "Mixed",
      age: 5
};

通过对象字面量创建对象,则当需要创建多个对象时,你需要在对象里面,为它添加很多属性和方法,而且某些属性和之前完全相同,你只是在不停的重用它,而且代码量大大增加。所以,还有一种创建对象的方法,是通过对象构造函数来创建的。它能帮助我们创建大量的对象,且创建不同对象时,不需要重用其属性,例如:

function Dog(name,breed,age){
         this.name = name;
         this.breed = breed;
         this.age = age;
}
var fido = new Dog("Fido","Mixed",5);
var spot = new Dog("Spot","Chihuahua",10);

在上面的代码中,我们定义了一个叫Dog的构造函数,它里面包含name,breed,age三个属性。因此我们创建不同的小狗对象时,只需要为每个不同的小狗对象传入不同的实参即可。


那么,构造函数的工作原理又是什么?

说说其工作原理

通过观看上面的代码,我们可以看到当创建对象时,都要写一个new运算符来调用构造函数。那么new运算符都做了什么呢?我们的视线都暂且停留在赋值运算的右边:1、new运算符创建了一个新的空对象。2、new设置this,使其指向刚刚创建的新对象。3、调用构造函数Dog,将三个实参传递给Dog的三个形参。4、构造函数Dog给新创建的this指向的对象的属性赋值。5、Dog函数执行完后,new返回this,指向新创建的对象的引用。这里需要注意的是,它会为你自动地返回this,你无需在代码中显示地返回。返回this后,我们将其赋值给变量,存储在变量中。

能为对象添加属性和方法,在构造函数中也一样能干。

对象在JS中是一个动态的结构,无论对象包含哪些属性和方法,其类型都是Object当用new运算符调用一个构造函数时,都将创建一个新的对象实例。由同一个构造函数创建的对象都是相同类型的对象。


当我们用构造函数创建对象时,如果出现了包含的形参过多怎么办?

针对这种情况,我们就要将对象字面量与对象构造函数相结合使用了,显然极端地使用某一种方式在某些问题上,根本行不通。例如:

function Car(make,model,year,color,passengers,convertible,mileage){
               ...
}
var cadi = new Car("GM","Cadillac",1955,"red",5,false,12892);

上面的例子能看出,使用构造函数创建对象时,需要传入的实参实在是过多了。如何解决这种问题?我们将所有实参放到一个对象字面量中,再将这个对象字面量传给函数。这就像通过一个容器来传递所有的值。例如:

var cadiparam = {
       make: "GM",
       model: "Cadillac",
       year: 1955,
       color: "red",
       passengers: 5,
       convertible: false,
       mileage: 12892
};
var cadi = new Car(cadiparam);

好像对于代码量来说,好像并没有啥改变。但对于在给构造函数传入实参时,能够避免出现不必要的错误。


好了,扯了那么久,又回到原型了。

当我们用上述的构造函数创建对象时,每个对象都有自己的副本,当在其中添加方法时,由于对象都有其副本,所以在运行阶段时,每个对象都将创建一组新的方法这会占用计算机的资源,并且影响应用程序的性能

我们可以通过扩展其他对象来创建对象,也就是通过原型对象来创建对象由原型对象创建而成的对象,它继承了原型对象的属性和方法,我们在这里可以将原型对象想象成父类,将由原型对象创建而成的对象想象成子类,那么子类就自然而然地继承了父类的所有方法及属性。在继承了原型的对象中,只需加入新的属性和方法,原型有的属性和方法不用重写,因为它继承了这些属性和方法。


继承的工作原理又是什么?

当对象实例调用了自己本身没有的属性和方法时,JavaScript如果在对象中找不到,那么将在原型中查找它。也就是说,查找的顺序一定是:先在对象实例中查找——>对象实例中找不到,就向继承链上方查找,也就是在原型中查找。继承原型并不意味着必须与它相同,当然可以重写原型的属性和方法。

如何创建一个原型?

构造函数中有一个属性prototype,该属性是一个指向原型的引用。

如何设置原型?

通过构造函数的一个属性prototype来访问原型对象。大致的顺序为:先创建一个构造函数,再获取其属性prototype,再为原型添加属性和方法,依次来设置原型。例如:

function Dog(name,breed,weight){
        this.name = name;
        this.breed = breed;
        this.weight = weight;
}
Dog.prototype.species = "Canine";
Dog.prototype.bark = function (){
        if(this.weight > 25) { 
               console.log(this.name + " says woof!");
      } else {
               console.log(this.name + " says yip!");
      }
  };

在上方代码中,我们为Dog原型中,新增了属性species和方法bark。在任何情况下,this都指向原始对象,即方法被调用的对象。原型是动态的,也就是说,当我们在原型中新增了一个属性和方法时,继承该原型的任何对象都能使用这个方法和属性。

如何判断使用的属性是包含在实例中还是原型中?

每个对象都有一个方法hasOwnProperty如果属性是在对象实例中定义的,则返回true,如果是在原型中定义的,则返回false。其参数为一个字符串

原型链

对象不仅可以继承一个原型的属性,还可以继承一个原型链。原型链中的继承原理,和上面说到的继承原理是一样的,先在对象实例中查找,找不到则沿着原型链向上查找

那么如何继承一个原型对象呢?

依然要用到new运算符,例如:

function Dog(name,breed,weight){
        this.name = name;
        this.breed = breed;
        this.weight = weight;
}
function ShowDog(name,breed,weight){
        this.name = name;
        this.breed = breed;
        this.weight = weight;
}
ShowDog.prototype = new Dog();

依据上方的代码 ,我们成功地创建了一个继承原型对象的对象,顺序依然是创建该对象的构造函数,通过函数名调用其属性prototype,然后将其属性prototype设置为一个新的对象实例。新创建的原型对象,依然是原来原型的一个对象实例。新的原型创建成功后,我们就可以给它添加新的属性和方法了。

值得注意的是,所有对象都是从Object派生而来,它是对象的始祖,实际上Object也有原型,其原型为Object.prototype。

结束语

《Head First JavaScript》这本书就此结束啦,在JavaScript中,一切几乎皆是对象,学习永远没有解脱的时候。

发表评论

渝公网安备 50010102001052号

/

渝ICP备2021009595号