摘要:中创建对象是如何实现的其实通过上面方式,使用构造函数声明实例的专属变量和方法,使用原型声明公用的实例和方法,已经是创建对象的完美解决方案了。
var cat = { name: "tom", info: this.name + ": 1212", getName: function() { return this.name; } };
注意上例属性info中,使用了this.name,这里的this指向window对象,请尽量避免在定义对象属性时使用表达式,而将有表达式的内容写入到函数中。
var dog = new Object(); dog.name = "tim"; dog.getName = function() { return dog.name; }
可以使用delete删除对象的属性和方法
delete dog.name;
在window作用域中,不能使用delete删除var, function定义的属性和方法,可以删除没有使用var, function定义的属性和方法
在实际使用当中,字面量创建对象虽然很有用,但是它并不能满足我们的所有需求,我们希望能够能够和其他后台语言一样创建一个类,然后声明类的实例就能够多次使用,而不用每次使用的时候都要重新创建它,于是,便有了工厂模式的出现。
function person(name) { var o = new Object(); o.name = name; o.getName = function() { return this.name; } return o; } var person1 = person("rose"); var person2 = person("jake");
这种模式在函数的内部创建了一个空对象,然后逐一添加属性和方法,最后返回,实现了对象得以复用的目的。但是存在2个很大的问题
无法识别对象的类型
console.log(person1 instanceof person); // false
每个对象调用的同名方法其实并不同一个方法
console.log(person1.getName == person2.getName); // false
其实就相当于每次声明对象都被重新创建,只不过写法上简单了一点而已。
var Person = function(name) { this.name = name; this.getName = function() { return this.name; } } var person1 = new Person("tom"); var person2 = new Person("tim");
使用var或者function声明函数都可以,只是我写例子的时候想到什么就写了什么,这个区别在这里不是重点
和工厂模式相比,自定义构造函数没有在函数内部显示的创建和返回对象,而是使用this,当然,看上去简洁了许多,那么它解决了工厂模式的什么问题呢?
console.log(person1 instanceof Person); // ture console.log(person1.getName == person2.getName); //false
从上面代码可以看出,对象的类别可以判断了,person1就是Person的对象,可是2个同名方法任然不是同一个方法,而是重新创建,其实构造函数内部的实现,可以将上面的代码写成这样来理解
var Person = function(name) { var this = {}; this.name = name; this.getName = function() { return this.name; } return this; }
看上去和工厂模式就有点像,只是this的声明和返回都是隐式的。下一步,我们将要介绍关键先生,原型
原型并没有那么神秘,因为在javascript中,它无处不在。为了了解原型,我们可以在chrome浏览器的console中,随意创建一个函数
function a(){}
然后继续输入
a.prototype
得到的结果如下
a { constructor: function a(), _proto_: Object }
没错,得到的这个a,就是关键先生原型了。每一个函数都有一个prototype属性,他就像一个指针一样指向它的原型,而每一个原型,都有一个constructor属性,指向他的构造函数。
那么原型在创建对象中有什么用呢?
一个例子,千锤百炼,如下
function Person(name) { this.name = name; } Person.prototype.getName = function() { return this.name; } var person1 = new Person("rose"); var person2 = new Person("Jake");
在这里,我们将属性写在了构造函数里,将令人头疼的方法写在原型里,看看上面的问题得到解决没有
console.log(person1 instanceof Person); // true console.log(person1.getName === person2.getName); // true
OK,问题完美解决。
当然也可以将属性写入原型中,但是如果那样的话,属性就会如同方法一样被公用了,因此一般来说,属性会写入构造函数之中,方法写入原型之中。当然,这视情况而定。
如果你想进一步了解原型,可以看下图。
当我们使用new Person时便会创建一个实例,比如这里的person1与person2,这里的实例中,会有一个_proto_属性指向原型。
原型中的查找机制
当我们使用实例person1调用方法person.getName()时,我们首先找的,是看看构造函数里面有没有这个方法,如果构造函数中存在,就直接调用构造函数的方法,如果构造函数不存在,才回去查找原型中是否存在该方法
function Cat(name) { this.name = name; this.age = 12; this.getName = function() { return "constructor"; } } Cat.prototype.name = "proto name"; Cat.prototype.getName = function() { return this.name; } var tim = new Cat("Tim"); console.log(tim.name); // tim console.log(tim.getName()); // constructor
可以看到上例中,当原型和构造函数中拥有同样的方法和属性的时候,构造函数中的被执行。
于是,这里便会有一个十分重要的概念需要理解,那就是this的指向问题。
在整个创建对象的过程当中,this到底指向谁?
function Person(name) { this.name = name; // this.getName = function() { // return "constructor"; // } } Person.prototype.getName = function() { return this.name; } Person.prototype.showName = function() { return this.getName(); } var rose = new Person("rose"); console.log(rose.showName()); //rose
其实在new执行时,构造函数中的this与原型中的this都被强行指向了new创建的实例对象。
如果需要写在原型上的方法很多的话,还可以这样来写,让写法看上去更加简洁
Person.prototype = { constructor: Person, getName: function(){}, showName: function(){}, ... }
constructor:Person这一句必不可少,因为{}也是一个对象,当使用Person.prototype = {}时,相当于重新定义了prototype的指向,因此手动修正{}的constructor属性,让他成为Person的原型。
其实通过上面方式,使用构造函数声明实例的专属变量和方法,使用原型声明公用的实例和方法,已经是创建对象的完美解决方案了。可是唯一的不足在于,每次创建实例都要使用new来声明。这样未免太过麻烦,如果jquery对象也这样创建,那么你就会看到一段代码中有无数个new,可是jQuery仅仅只是使用了$("xxxx")便完成了实例的创建,这是如何做到的呢?
还是按照惯例,先贴代码。
(function(window, undefined) { var Person = function(name) { return new Person.fn.init(name); } Person.prototype = Person.fn = { constructor: Person, init: function(name) { this.name = name; return this; }, getName: function() { return this.name; } } Person.fn.init.prototype = Person.fn; window.Person = window.$ = Person; })(window); console.log($("tom").getName());
一步一步来分析
首先为了避免变量污染,使用了函数自执行的方式。这种方式让javascript代码具备了模块的特性,因此大多数js库都会这样做
(function(){ ... })()
传入window参数,是为了让jquery对象在外window中可以被访问,因此有如下一句代码
window.Person = window.$ = Person;
这样我们就可以直接使用$来调用Person构造函数
关键问题在于,真正的构造函数并不是Person,而是Person原型中的init方法。其中的复杂关系,我们借助下图来分析了解,表达能力实在有限,也不知道如何才能表达的更加简洁易懂。
当外部调用$().getName()时,函数内部的执行顺序如下
new Person.fn.init() // 而init的原型,通过下面一句指向了Person的原型 Person.fn.init.prototype = Person.fn; // 于是就可以调用原型中的getName方法了
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/86189.html
摘要:字面量方式这是最简单最基本的一种方法。简单的构造函数方式通过这样的形式创建对象。结合上面的简单构造函数和原型,一个完整的构造函数应该是这样的还有一种方法就是提供的简单实现下中的,,创建一个对象谈谈对象的理解。避免使用表达式又称动态属性。 要点:数据类型、面向对象、继承、闭包、插件、作用域、跨域、原型链、模块化、自定义事件、异步装载回调、模板引擎、Nodejs等。 JS基本类型有什么?引...
摘要:接下来该填表了生成行和单元格为了填充表格可以遵循同样的方法,但这次我们需要迭代数组中的每个对象。对于每个对象,我们可以使用生成单元格。 翻译:疯狂的技术宅原文:https://www.valentinog.com/bl... 本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章 怎样用原生 JavaScript 生成表格需?本文告诉你答案!...
摘要:在中函数是一等对象,它们不被声明为任何东西的一部分,而所引用的对象称为函数上下文并不是由声明函数的方式决定的,而是由调用函数的方式决定的。更为准确的表述应该为当对象充当函数的调用函数上下文时,函数就充当了对象的方法。 引言:当理解了对象和函数的基本概念,你可能会发现,在JavaScript中有很多原以为理所当然(或盲目接受)的事情开始变得更有意义了。 1.JavaScript...
摘要:点击直达前文译一个小时搭建一个全栈应用框架上如果没有,但还是要继续学习本教程,可以到我的页面下载代码。从服务器返回随机语言的每当我们与服务器上的端点进行通话时,为了能够请求一个随机的欧洲语言,必须更改文件中的功能。 翻译:疯狂的技术宅原文标题:Creating a full-stack web application with Python, NPM, Webpack and Reac...
摘要:点击直达前文译一个小时搭建一个全栈应用框架上如果没有,但还是要继续学习本教程,可以到我的页面下载代码。从服务器返回随机语言的每当我们与服务器上的端点进行通话时,为了能够请求一个随机的欧洲语言,必须更改文件中的功能。 翻译:疯狂的技术宅原文标题:Creating a full-stack web application with Python, NPM, Webpack and Reac...
阅读 2012·2021-09-29 09:35
阅读 1948·2019-08-30 14:15
阅读 2973·2019-08-30 10:56
阅读 953·2019-08-29 16:59
阅读 570·2019-08-29 14:04
阅读 1300·2019-08-29 12:30
阅读 1019·2019-08-28 18:19
阅读 508·2019-08-26 11:51