创建对象
js创建对象的方法有几种:
1,工厂模式。
2,构造函数模式。
3,原型模式。
4,组合使用构造函数模式和原型模式
(一):工厂模式
function createPerson (name){ var obj = new Object(); obj.name = name; obj.show = function(){ alert(this.name); } return obj; } var person = createPerson("ym"); //不需要使用new person.show();
(二),构造函数模式:
构造函数的函数名大写开头,其他函数都是小写
//构造函数模式 function Person(name, age){ this.name = name; var sex = sex //私有 this.show = function(){ alert(sex); } } var person1 = new Person("ym", "nan"); alert(person1.name); //ym alert(person2.sex) //undefined person1.show(); //nan
//构造方法模式与工厂模式的区别:
1,没有显示创建对象。
2,直接赋值给 this 对象。
3,没有 return 语句。
//构造函数的问题:
当对象有多个实例时,这些实例的所有方法都是不同的,因为它都会创建一遍。
//构造函数模式 function Person(name, sex){ this.name = name; var sex = sex; //私有 this.show = function(){ alert(sex); } } var person1 = new Person('ym', 'nan'); var person2 = new Person('jh', 'nv'); // alert(person1 instanceof Person); //true alert(person1.show == person2.show); //false
(三),原型模式
//优点:可以让所有对象实例共享它所包含的属性和方法。
// 6、原型模式 function Person2(){} Person2.prototype = { name : "ym", sex : 'nan', show : function(){ alert(this.name); } } var a = new Person2(); var b = new Person2(); alert(a.show == b.show); //true
//原型模式的问题:
function Person2(){} Person2.prototype = { name : "ym", sex : 'nan', love : ['a', 'b'], show : function(){ alert(this.name); } } var a = new Person2(); a.love.push('c'); var b = new Person2(); a.love.push('d'); // alert(a.show == b.show); alert(a.love); //abcd alert(b.love); //abcd
(四)、组合使用原型模式和构造函数模式
//通过以上的例子我们可以知道,构造函数模式,创建的方法都是不同的,都是实例自己拥有的,而原型模式定义的属性和方法是共享的,那么结合起来使用真是perfect。
// 6、混合模式 function Perfect(name, sex){ this.name = name; this.sex = sex; this.love = ['a' , 'b']; } Perfect.prototype = { constructor : Perfect, show : function(){ alert(this.love); } } var a = new Perfect('a', 'nan'); var b = new Perfect('b', 'nv'); a.love.push('c'); b.love.push('d'); a.show(); //abc b.show(); //abd
详解this
this 在 javascript 可以算是一个很神奇的对象,没错 this 是一个对象。在全局环境中 this===window 是会返回 true 的。
除了全局执行环境以外,我们还提到了另外一种执行环境,也就是函数。每一个函数都有一个this对象,但有时候他们所代表的值是不一样的,主要是这个函数的调用者来决定的。我们来看一下以下几种场景:
(1)、函数
function f1(){ return this; } f1() === window; // global object
因为当前函数在全局函数中运行,所以函数中的 this 对象指向了全局变量对象,也就是window。这种方式在严格模式下会返回 undefined。
(2)、对象方法
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
在对象方法中,this 指向当前这个实例对象。ps:不管这个函数在哪里什么时候或者怎么样定义,只要它是一个对象实例的方法,那么它的this都是指向这个对象实例的。
var o = { prop: 37 }; var prop = 15; function independent() { return this.prop; } o.f = independent; console.log(independent()); // logs 15 console.log(o.f()); // logs 37
区别:上面的函数independent如果直接执行,this是指向全局执行环境,那么this.prop是指向我们的全局变量prop的。但是如果将independent设为对象o的一个属性,那么independent中的this就指向了这个实例,同理this.prop就变成了对象o的prop属性。
(3)、构造函数
在构造函数中,this 对象是指向这个构造函数实例化出来的对象。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol',16,'designer');
当我们实例化Person得到p1时,this指向p1。当我们实例化Person得到p2的时候,this是指向p2的。
(4)、利用 call 和 apply
当我们用 call 和apply 去调用某一个函数的时候,这个函数中的 this 对象会被绑定到我们指定的对象上。而 call 和 apply 的主要区别就是apply要求传入一个数组作为参数列表。
function add(c, d) { return this.a + this.b + c + d; } var o = { a: 1, b: 3 }; // 第一个参数会被绑定成函数add的this对象 add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // 第二个参数是数组作为arguments传入方法add add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
(5)、在bind方法中
bind方法是 存在于function的原型中的 Function.prototype.bind,也就是说所有的function都会有这个方法。但我们调用某一个方法的bind的时候,会产生一个和原来那个方法一样的新方法,只不过this是指向我们传得bind的第一个参数。
function f() { return this.a; } var g = f.bind({ a: "azerty" }); console.log(g()); // azerty var o = { a: 37, f: f, g: g }; console.log(o.f(), o.g()); // 37, azerty
详解 prototype
prototype即原型,也是Javascrip中一个比较重要的概念。在说原型之前呢,我们需要回顾一下之前的构造函数模式。在我们用构造函数去创建对象的时候主要是利用了this的特性。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { alert(this.name); }; } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol', 17, 'designer');
我们上面还讲到了当用Person实例化p1的时候Person中的this是指向p1的,当实例化p2的时候呢,this是指向p2的。那也就是说,p1和p2中的sayName虽然起到了同样的作用,但是实际上他们并非是一个函数。
也就是说他们内存堆中是存在多份拷贝的,而不是在栈中引用地址的拷贝。先不说这符不符合面向对象的思想,至少这对于内存来说也是一种浪费。而解决办法就是我们要讨论的原型。
什么是原型
在Javascript中的每一个函数,都会有一个原型对象,这个原型对象和我们普通的对象没有区别。只不过默认会有一个constructor属性指向这个函数。 同时,所有这个函数的实例都会有一个引用指向这个原型对象。如果不太清楚,那就看看下面这张图吧:
以上就是构造函数,构造函数原型,以及实例之间的关系。以我们的Person构造函数为例,所有Person的实例(p1,p2)都舒服一个prototype属性指向了Person构造函数prototype对象。如此一来,我们就可以把方法写在原型上,那么我们所有的实例就会访问同一个方法了。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; Person.prototype.sayName = function () { alert(this.name); } } var p1 = new Person('Jesse', 18, 'coder'); var p2 = new Person('Carol', 17, 'designer'); alert(p1.sayName == p2.sayName); // true
什么是原型链
大家还记得作用域链么?如果不记得,请自觉到第二篇中去复习(作用域和作用域链)。简单的来说,我们在一个执行环境中访问某个变量的时候如果当前这个执行环境中不存在这个变量,那么会到这个执行环境的包含环境也就是它的外层去找这个变量,外层还找不到那就再外一层,一直找到全局执行环境为止,这就是作用域链。而原型链有点类型,只不过场景换到了我们的对象实例中,如果我在一个实例中找某一个属性,这个实例中没有,那就会到它的原型中去找。记住,我们上面说了,原型也是一个对象,它也有自己的原型对象,所以就行成了一个链,实例自己的原型中找不到,那就到原型的原型对象中去找,一直向上延伸到Object的原型对象,默认我们创建的函数的原型对象它自己的原型对象是指向Object的原型对象的,所以这就是为什么我们可以在我们的自定义构造函数的实例上调用Object的方法(toString, valueOf)。
利用原型实现继承
其实我们上面已经讲了继承在Javascript中的实现,主要就是依靠原型链来实现的。所有的实例是继承自object就是因为在默认情况下,我们所有创建函数的原型对象的原型都指向了object对象。同理,我们可以定义自己的继承关系。
function Person(name, age, job) { this.name = name; this.age = age; } Person.prototype.sayName = function () { alert(this.name); } function Coder(language){ this.language = language; } Coder.prototype = new Person(); //将 Coder 的原型指向一个Person实例实现继Person Coder.prototype.code = function () { alert('I am a '+ this.language +' developer, Hello World!'); } function Designer() { } Designer.prototype = new Person(); //将 Desiger 的原型指向一个Person实例实现继Person Designer.prototype.design = function () { alert('其实我只是一个抠图工而已。。。。'); } var coder = new Coder('C#'); coder.name = 'Jesse'; coder.sayName(); //Jesse coder.code(); // I am a C# developer, Hello World! var designer = new Designer(); designer.name = 'Carol'; designer.sayName(); // Carol designer.design(); // 其实我只是一个抠图工而已。。。。
原型链中的问题
由于原型对象是以引用的方式保存的,所以我们在赋值的时候要特别注意,一不小心就有可能把之前赋的值给赋盖了。比如上面的代码中,我们先写原型方法,再实现继承,那我们的原型方法就没有了。
function Coder(language){ this.language = language; } Coder.prototype.code = function () { alert('I am a '+ this.language +' developer, Hello World!'); } Coder.prototype = new Person(); //这里会覆盖上面所有的原型属性和方法 var coder = new Coder('C#'); coder.name = 'Jesse'; coder.sayName(); coder.code(); // 这里会报错,找不到code方法。