这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
下面小编就为大家带来一篇浅谈 Javascript 中的函数、this 以及原型。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
关于函数
在 Javascript 中函数实际上就是一个对象,具有引用类型的特征,所以你可以将函数直接传递给变量,这个变量将表示指向函数 "对象" 的指针,例如:
- function test(message){
- alert(message);
- }
- var f = test;
- f('hello world');
你也可以直接将函数申明赋值给变量:
- var f = function(message){
- alert(message);
- };
- f('hello world');
在这种情况下,函数申明中可以省略函数名称,因为此时名称已经没有任何意义,我们可直接通过变量 f 来调用函数。
通过 Function 类型,我们可以更好地理解函数即对象:
- var f = new Function("message","alert(message);");
- f('hello world');
关于 this
this 可以看成调用函数的实际作用域上下文。比较以下函数的执行结果:
- function test(){
- this.property = 'hello world';
- }
- test();
- alert(window.property); //由于在全局范围内调用,test函数中的this实际指向全局对象(window)
- var obj = {};
- test.call(obj); //通过call第一个参数指定执行上下文范围,所以test函数中this指向obj实例。
- alert(obj.property);
- var obj2 = {};
- obj2.test2 = test; //将obj2实例方法test指向 全局test方法
- obj2.test2(); //由于是在obj2上调用test方法,所以test函数中的this也指向了obj2实例
- alert(obj2.property);
定义类型
在 Javascript 中可以定义构造函数,构造函数与一般函数没有任何区别,在创建实例时,如果我们使用了 new 关键字,那么这个函数就具有构造函数的特性,否则就是一般函数,如下所示,我们定义了一个 Person 类型:
- function Person(){
- this.name = 'xfrog';
- this.Say = function(){
- alert(this.name);
- };
- }
当使用 new 关键字时,可以创建一个新的 Person 对象实例:
- var p1 = new Person();
- p1.Say();
如果不使用 new 关键字,将直接执行 Person 函数,由于执行上下文为全局范围,故 name 属性和 Say 方法将被添加到 window 对象:
- Person();
- Say();
- window.Say();
原型
注意上述 Person 的定义方式,当使用 new 来创建 Person 实例时,将会执行 Person 构造函数,也就是会声明 name 属性和 Say 方法,这样可能产生效率问题,注意以下代码:
- var p1 = new Person();
- var p2 = new Person();
- var test = p1.Say == p2.Say;
比较 p1 和 p2 两个 Say 函数指针,返回 false,表示每个 Person 实例中的 Say 方法都是独立的,而事实上 Say 函数的功能是完全一样的,我们完全没有必要为每个对象重新分配 Say 函数 "对象",如果 Person 实例很多,将会造成大量的内存耗用。
如果将 Say 函数提取出来放入全局执行范围,似乎可解决次问题:
- function Person(){
- this.name = 'xfrog';
- this.Say = say;
- }
- function say(){
- alert(this.name);
- }
- var p1 = new Person();
- var p2 = new Person();
- alert(p1.Say == p2.Say);
- p1.name = 'wang';
- p1.Say();
由于 this 始终和执行上下文相关,p1 和 p2 实例中的 Say 方法中会正确地返回对应实例的 name 属性。但是,使用此方式有违面向对象的思想,也失去了类型密封的原则。还会造成大量的全局函数。
为了解决这些缺点,Javascript 引出了原型的概念,简单理解,原型可以看成是类型的共享区,原型本身是一个对象,而对象中的属性对于类型来说是共享的。Javascript 中每个类型通过 prototype 属性来表示原型,通过这个属性可指定共享方法:
- function Person(){
- }
- Person.prototype.name = 'xfrog';
- Person.prototype.Say = function(){
- alert(this.name);
- };
- var p1 = new Person();
- var p2 = new Person();
- alert(p1.Say == p2.Say); //返回true
为什么这里可以通过 p1.Say 来访问 Say 方法呢?这是因为 ECMAScript 标准规定了类型属性的查找顺序:先在类型的实例上查找,如果没有则继续在类型原型上查找,这一查找路径采用短路算法,即找到首个后即返回,考虑如下代码:
- function Person() {
- this.name = 'wang';
- }
- Person.prototype.name = 'xfrog';
- Person.prototype.Say = function() {
- alert(this.name);
- }
- var p1 = new Person();
- p1.Say(); //将返回wang
上面提到 prototype 实际上是一个对象,那么我们是否可以直接访问呢? 在一些浏览器实现(如 Chrome、Fixfox 等)的确可通过实例的__proto__属性来访问内部的 prototype 对象,这种特征表明 Javascript 引擎在每个对象的内部都是通过一个变量来保存对 prototype 的引用,这保证了 prototype 对应整个类型的实例来说是共享的,例如,你可在 Chrome 浏览器内使用如下方式来访问 Say 方法:
- p1.__proto__["Say"]();
由于原型是一个对象,我们可以直接将一个对象赋值给 prototype:
- function Person() {}
- Person.prototype = {
- name: 'xfrog',
- Say: function() {
- alert(this.name);
- }
- };
注意这个方式下,实际上是完全替换了 Person 的 prototype,这与上面 Person.prototype.name 方式还是有细微差异的,这是因为任何类型,Javascript 引擎都会添加默认的 prototype,在这个 prototype 中包含一个对构造函数的引用,即原型对象属性 constructor,所以通常使用替代 prototype 方式时,我们需要手动加上 constructor 属性:
- Person.prototype = {
- constructor: Person,
- name :'xfrog',
- Say:function(){
- alert(this.name);
- }
- }
注意,由于 prototype 对于整个类型是共享的,那么在 prototype 中的引用类型可能会存在问题,前面的 Say 函数作为一个对象,也是引用类型,所以每个实例中的 Say 都指向原型对象中的同一个函数,这本身没有问题,也是我们使用原型的初衷,但对于其他引用对象,可能结果并不是我们想要的:
- function Person() {}
- Person.prototype = {
- name: 'xfrog',
- obj: {
- age: 18
- },
- Say: function() {
- alert(this.obj.age);
- }
- };
- var p1 = new Person();
- var p2 = new Person();
- p1.obj.age = 20;
- p1.Say();
- p2.Say();
p2.Say 返回的是 20,这是因为 obj 属性作为原型属性是共享的,在内存中只存在一个实例,所以通过 p1 修改后,p2 只能得到修改后的状态。如果要避免此情况,可将 obj 属性放到实例中:
- function Person() {
- this.obj = {
- age: 18
- };
- }
来源: http://www.phperz.com/article/17/0512/331345.html