1.使用Object或对象字面量建立对象前端
2.工厂模式建立对象面试
3.构造函数模式建立对象数组
4.原型模式建立对象app
JS中最基本建立对象的方式:函数
var student = new Object(); student.name = "easy"; student.age = "20";
这样,一个student对象就建立完毕,拥有2个属性name
以及age
,分别赋值为"easy"
和20
。ui
若是你嫌这种方法有一种封装性不良的感受。来一个对象字面量方式建立对象。this
var sutdent = { name : "easy", age : 20 };
这样看起来彷佛就完美了。可是立刻咱们就会发现一个十分尖锐的问题:当咱们要建立同类的student1,student2,…,studentn时,咱们不得不将以上的代码重复n次....spa
var sutdent1 = { name : "easy1", age : 20 }; var sutdent2 = { name : "easy2", age : 20 }; ... var sutdentn = { name : "easyn", age : 20 };
有个提问?能不能像工厂车间那样,有一个车床就不断生产出对象呢?咱们看”工厂模式”。prototype
JS中没有类的概念,那么咱们不妨就使用一种函数将以上对象建立过程封装起来以便于重复调用,同时能够给出特定接口来初始化对象指针
function createStudent(name, age) { var obj = new Object(); obj.name = name; obj.age = age; return obj; } var student1 = createStudent("easy1", 20); var student2 = createStudent("easy2", 20); ... var studentn = createStudent("easyn", 20);
这样一来咱们就能够经过createStudent函数源源不断地”生产”对象了。看起来已经高枕无忧了,但贪婪的人类总有不知足于现状的天性:咱们不只但愿”产品”的生产能够像工厂车间通常源源不断,咱们还想知道生产的产品到底是哪种类型的。
好比说,咱们同时又定义了”生产”水果对象的createFruit()函数:
function createFruit(name, color) { var obj = new Object(); obj.name = name; obj.color = color; return obj; } var v1 = createStudent("easy1", 20); var v2 = createFruit("apple", "green");
对于以上代码建立的对象v一、v2,咱们用instanceof操做符去检测,他们通通都是Object类型。咱们的固然不知足于此,咱们但愿v1是Student类型的,而v2是Fruit类型的。为了实现这个目标,咱们能够用自定义构造函数的方法来建立对象
在上面建立Object这样的原生对象的时候,咱们就使用过其构造函数:
var obj = new Object();
在建立原生数组Array类型对象时也使用过其构造函数:
var arr = new Array(10); //构造一个初始长度为10的数组对象
在进行自定义构造函数建立对象以前,咱们首先了解一下构造函数
和普通函数
有什么区别。
一、实际上并不存在建立构造函数的特殊语法,其与普通函数惟一的区别在于调用方法。对于任意函数,使用new操做符调用,那么它就是构造函数;不使用new操做符调用,那么它就是普通函数。
二、按照惯例,咱们约定构造函数名以大写字母开头,普通函数以小写字母开头,这样有利于显性区分两者。例如上面的new Array(),new Object()。
三、使用new操做符调用构造函数时,会经历(1)建立一个新对象;(2)将构造函数做用域赋给新对象(使this指向该新对象);(3)执行构造函数代码;(4)返回新对象;4个阶段。
ok,了解了构造函数
和普通函数
的区别以后,咱们使用构造函数将工厂模式
的函数重写,并添加一个方法属性:
function Student(name, age) { this.name = name; this.age = age; this.alertName = function(){ alert(this.name) }; } function Fruit(name, color) { this.name = name; this.color = color; this.alertName = function(){ alert(this.name) }; }
//这样咱们再分别建立Student和Fruit的对象
var v1 = new Student("easy", 20); var v2 = new Fruit("apple", "green");
//这时咱们再来用instanceof操做符来检测以上对象类型就能够区分出Student以及Fruit了:
alert(v1 instanceof Student); //true alert(v2 instanceof Student); //false alert(v1 instanceof Fruit); //false alert(v2 instanceof Fruit); //true alert(v1 instanceof Object); //true 任何对象均继承自Object alert(v2 instanceof Object); //true 任何对象均继承自Object
/*
Stuent,Fruit充当构造函数
this 代指当前对象
建立对象时须要使用 new
*/
这样咱们就解决了工厂模式
没法区分对象类型的尴尬。那么使用构造方法来建立对象是否已经完美了呢?使用构造器函数一般在js中咱们来建立对象。
咱们会发现Student和Fruit对象中共有一样的方法,当咱们进行调用的时候这无疑是内存的消耗。
咱们彻底能够在执行该函数的时候再这样作,办法是将对象方法移到构造函数外部:
function Student(name, age) { this.name = name; this.age = age; this.alertName = alertName; } function alertName() { alert(this.name); } var stu1 = new Student("easy1", 20); var stu2 = new Student("easy2", 20);
在调用stu1.alertName()时,this对象才被绑定到stu1上。
咱们经过将alertName()函数定义为全局函数,这样对象中的alertName属性则被设置为指向该全局函数的指针。由此stu1和stu2共享了该全局函数,解决了内存浪费的问题
可是,经过全局函数的方式解决对象内部共享的问题,终究不像一个好的解决方法。若是这样定义的全局函数多了,咱们想要将自定义对象封装的初衷便几乎没法实现了。更好的方案是经过原型对象模式来解决。
原型链甚至原型继承,是整个JS中最难的一部分也是最很差理解的一部分,在这里因为咱们课程定位的缘由,若是对js有兴趣的同窗,能够去查阅一下相关JS原型的一些知识点。更加有助于你之后前端JS的面试。
function Student() { this.name = 'easy'; this.age = 20; } Student.prototype.alertName = function(){ alert(this.name); }; var stu1 = new Student(); var stu2 = new Student(); stu1.alertName(); //easy stu2.alertName(); //easy alert(stu1.alertName == stu2.alertName); //true 两者共享同一函数
或者这样写
function Foo (name,age) { this.Name = name; this.Age = age; } Foo.prototype = { GetInfo: function(){ return this.Name + this.Age }, Func : function(arg){ return this.Name + arg; } }