1.javascript创建对象

  创建新对象有两种不同的方法:

  1. 定义并创建对象的实例

  

person=new Object();
person.firstname="Bill";
person.lastname="Gates";
person.age=56;
person.eyecolor="blue";
等价于:
person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"};

  2.使用函数来定义对象,然后创建新的对象实例 

function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
}

创建新实例:

var myFather=new person("Bill","Gates",56,"blue");

  原因:这时,javascript设计者想到C++和Java使用new命令时,都会调用"类"的构造函数(constructor)。他就做了一个简化的设计,在Javascript语言中,new命令后面跟的不是类,而是构造函数。

2 实现继承

  由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。

  实例名.__proto__=类.prototype

  2.1 new创建对象过程的分解:

function Foo() {}
var foo=new Foo();

分解为三步:

a. var foo={};
b. foo.__proto__=Foo.prototype;
c. Foo.call(p);

那么什么是__proto__?每一个通过函数和new操作符生成的对象都具有一个属性__proto__, 这个属性保存了创建它的构造函数的prototype属性的引用。按照标准,__proto__是个私有属性,但是在Firefox浏览器的脚本引擎中,它成为了一个可以访问的公有属性。

2.2原型链图

理解如下代码:

第一段:

<script type="text/javascript" lang="javascript">
var str="string";
function Foo() {var a=123;}
var foo=new Foo(); alert(str.__proto__);//empty
alert(str.constructor);//function String() { [native code] }
alert(str.__proto__.constructor);//function String() { [native code] } alert(str.__proto__===String.prototype);//true
alert(str.__proto__.__proto__===Object.prototype);//true
alert(str.__proto__.__proto__.__proto__===null);//true alert(Foo.__proto__);//function () { }
alert(Foo.constructor);//function Function() { [native code] }
alert(Foo.__proto__.constructor);//function Function() { [native code] }
alert(Foo.__proto__.__proto__);//[object Object] alert(Foo.__proto__===Function.prototype);//true
alert(Foo.__proto__.__proto__===Object.prototype);//true
alert(Foo.__proto__.__proto__.__proto__===null);//true alert(foo.__proto__);//[object Object]
alert(foo.constructor);//function Foo() {var a=123;}
alert(foo.__proto__.constructor);//function Foo() {var a=123;}
alert(foo.__proto__.__proto__);//[object Object] alert(foo.__proto__===Foo.prototype);//true
alert(foo.__proto__.__proto__===Object.prototype);//true
alert(foo.__proto__.__proto__.__proto__===null);//true
</script>

第二段:

<script type="text/javascript" lang="javascript">
alert(Object.__proto__);//function () { }
alert(Object.__proto__.__proto__);//[object Object]
alert(Object.__proto__.__proto__.__proto__);//null alert(Object.__proto__===Function.prototype);//true
alert(Object.__proto__.__proto__===Object.prototype);//true
alert(Object.__proto__.__proto__.__proto__===null);//true alert(Function.__proto__);//function () { }
alert(Function.__proto__.__proto__);//[object Object]
alert(Function.__proto__.__proto__.__proto__);//null alert(Function.__proto__===Function.prototype);//true
alert(Function.__proto__.__proto__===Object.prototype);//true
alert(Function.__proto__.__proto__.__proto__===null);//true
</script>

结论:

a. 在JavaScript中,一切的一切都是对象,它们全部继承自Object,或者说所有对象原型链的根节点都是Object.prototype。

b. 透彻理解JavaScript的原型链机制是非常重要的,一旦掌握了它,不管一个对象有多么的复杂,你总能够轻而易举地的将它攻破。

c. prototype只是一个假象,它在原型链中只是一个辅助角色,换句话说,它只在new的时候有着一定的价值,但是原型链的本质,其实在于__proto__!

 2.3揭开Javascript属性constructor/prototype的底层原理

当我们定义一个函数时,JavaScript内部会执行如下几个动作:
为该函数添加一个原形属性(即prototype对象).

为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的一个引用。

这样当我们把函数F作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数F)的prototype对象的一个属性__proto__,所以我们在每一个对象实例中就可以访问构造函数的prototype所有拥有的全部属性和方法,就好像它们是实例自己的一样。

当然该实例也有一个constructor属性了(从prototype那里获得的),这时候constructor的作用就很明显了,因为在这时,每一个对象实例都可以通过constrcutor对象访问它的构造函数,请看下面代码:

var f = new F();
alert(f.constructor === F);// output true
alert(f.constructor === F.prototype.constructor);// output true

原型链继承,由于constructor存在于prototype对象上,因此我们可以结合constructor沿着原型链找到最原始的构造函数,如下面代码:

function Base() {}
// Sub1 inherited from Base through prototype chain
function Sub1(){}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;
Sub1.superclass = Base.prototype;
// Sub2 inherited from Sub1 through prototype chain
function Sub2(){}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;
Sub2.superclass = Sub1.prototype;
// Test prototype chain
alert(Sub2.prototype.constructor);
// function Sub2(){}
alert(Sub2.superclass.constructor);
// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);
// function Base(){}

constructor易变,那是因为函数的prototype属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:

function F() {}
F.prototype = {
_name: 'Eric',
getName: function() {
return this._name;
}
};

初看这种方式并无问题,但是你会发现下面的代码失效了:
var f = new F();

alert(f.constructor === F); // output false

怎么回事?F不是实例对象f的构造函数了吗?

当然是!只不过构造函数F的原型被开发者重写了,这种方式将原有的prototype对象用一个对象的字面量{}来代替。

而新建的对象{}只是Object的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个constructor属性,因为这是function创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。

然而你会发现constructor并不是不存在的,下面代码可以测试它的存在性:

alert(typeof f.constructor == 'undefined');// output false

既然存在,那这个constructor是从哪儿冒出来的呢?

我们要回头分析这个对象字面量{}。

因为{}是创建对象的一种简写,所以{}相当于是new Object()。

那既然{}是Object的实例,自然而然他获得一个指向构造函数Object()的prototype属性的一个引用__proto__,又因为Object.prototype上有一个指向Object本身的constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,下面代码可以验证其结论:

alert(f.constructor === Object.prototype.constructor);//output true

alert(f.constructor === Object);// also output true

一个解决办法就是手动恢复他的constructor,下面代码非常好地解决了这个问题:

function F() {}
F.prototype = {
constructor: F, /* reset constructor */
_name: 'Eric',
getName: function() {
return this._name;
}
};

之后一切恢复正常,constructor重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个constructor?它又是哪儿来的?
细心的朋友会发现,像JavaScript内建的构造函数,如Array, RegExp, String, Number, Object, Function等等居然自己也有一个constructor:
alert(typeof Array.constructor != 'undefined');// output true
经过测试发现,此物非彼物它和prototype上constructor不是同一个对象,他们是共存的:
alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为构造函数也是函数。
是函数说明它就是Function构造函数的实例对象,自然他内部也有一个指向Function.prototype的内部引用__proto__啦。
因此我们很容易得出结论,这个constructor(构造函数上的constructor不是prototype上的)其实就是Function构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
OK, constructor从此真相大白,你不在对它陌生了~

3.函数和对象区别

alert(Object instanceof Function);//true
alert(Function instanceof Object);//true
alert(Function instanceof Function);//still true
alert(Object instanceof Object);//still true

在看如下例子:

function a(){
  this.a1 = 1;
}
var aa = new a();//通过函数名创建一个对象,而函数本身也是一个对象。(函数也称对象构造器,对象构造器是一个对象,但对象未必是对象构造器)
alert(a instanceof Object);//true

alert(a) //打印出函数的内容为:function a(){this.a1 = 1;}

alert(a.prototype); //打印出Object,说明函数有内置对象prototype
alert(aa.prototype);//打印出undefined,说明一般对象没有prototype

  总结:

  函数特点:

  函数也称对象构造器,对象构造器是一个对象,但对象未必是对象构造器。

 

4.Javascript全局变量和局部变量

在函数外部定义的为全局变量,不管加不加var;

在函数内部,不加var的,实际为全局变量,例子如下:

function f1(){
    n=999;
  }
  f1();
  alert(n); //

删除全局变量: delete 变量名;(局部变量无法删除)

如果只是使用变量test,那么三种方式将没有什么区别。比如:alert(test) 都将显示5。但三种方式在某些情况下还是有区别的。分别按以上三种方式声明三个变量a1,a2,a3。

1
2
3
a1 = 11;
var a2 = 22;
window.a3 = 33;

1.for in window

1
2
3
4
5
for(a in window){
    if(a=='a1'||a=='a2'||a=='a3'){
        alert(a)
    }
}

IE6/7/8/9:只弹出了a3,说明通过第一,二种方式声明的全局变量通过for in window时将获取不到。
Firefox/Chrome/Safari/Opera :a1,a2,a3都弹出了,说明三种方式声明的全局变量,通过for in window时都能获取到。

2,delete

1
2
3
4
5
6
7
8
9
10
11
try {
    alert(delete a1);
}catch(e){alert('无法delete a1')}
 
try{
    alert(delete a2);
}catch(e){alert('无法delete a2')}
 
try{
    alert(delete a3);
}catch(e){alert('无法delete a3')}

结果如下

可以看到,
1,delete a2所有浏览器都是false。即通过var声明的变量无法删除,所有浏览器表现一致。这在犀牛书上也有提到。
2,通过window.a3方式声明的全局变量在IE6/7/8中均无法删除,IE9/Firefox/Chrome/Safari/Opera中却可以。

虽然有以上两点不同,但当用in运算时,都返回true。

1
2
3
alert('a1' in window);//true
alert('a2' in window);//true
alert('a3' in window);//true

用with打开对象window闭包时,所有浏览器也表现一致,如下

1
2
3
4
5
6
7
8
9
10
11
with(window){
    if(a1){
        alert(a1);//11
    }
    if(a2){
        alert(a2);//22
    }
    if(a3){
        alert(a3);//33
    }  
}

5.Javascript闭包

闭包就是能够读取其他函数内部变量的函数。

书本上对闭包的羞涩解释:闭包(closure)是一个函数,通常也被称为闭包函数或绑定函数,该函数运行在一个特定的环境中,该环境定义了一些本地变量,当该函数被调用时,仍可以使用这些本地变量。

其实闭包的显著特征就是当一个函数在不位于它所处环境(变量作用范围)中被调用时,仍能够使用本地变量。下面来看看JavaScript中典型的两种闭包应用。

两大用处:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

闭包创建的条件:当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,如果内部函数引用了位于外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被 释放,因为闭包需要它们.

例子:

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); //
  nAdd();
  result(); //

这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个

匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

Javascript原理的更多相关文章

  1. [剖析Javascript原理]1.原生数据类型

    一.原生数据类型 JS共有5种原生数据类型: Boolean true或者false String 字符串,在单引号或者双引号之间(不存在字符类型) Number 整数或者浮点数 Null 空 und ...

  2. 浏览器解析JavaScript原理

    1.浏览器解析JavaScript原理特点: 1.跨平台 2.弱类型 javascript 定义的时候不需要定义数据类型,数据类型是根据变量值来确定的.    var a = 10; 数字类型    ...

  3. JavaScript ==原理与分析

    JavaScript原始类型 ECMAScript 有 5 种原始类型(primitive type),即 Undefined.Null.Boolean.Number 和 String. typeof ...

  4. js 计算当年还剩多少时间的倒数计时 javascript 原理解析【复制到编辑器查看推荐】

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. YUI Compressor 压缩 JavaScript 原理-《转载》

    YUI Compressor 压缩 JavaScript 的内容包括: 移除注释 移除额外的空格 细微优化 标识符替换(Identifier Replacement) YUI Compressor包括 ...

  6. JavaScript原理学习

    悟透JavaScript(理解JS面向对象的好文章) http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html Javascript ...

  7. JavaScript的计时器的工作原理

    最近都在看一些JavaScript原理层面的文章,恰巧看到了jQuery的作者的一篇关于JavaScript计时器原理的解析,于是诚惶诚恐地决定把原文翻译成中文,一来是为了和大家分享,二来是为了加深自 ...

  8. WebViewJavascriptBridge-Obj-C和JavaScript互通消息的桥梁

    转载至:http://www.cocoachina.com/ios/20150629/12248.html 译者:@coderyi9 本文翻译自Marcus Westin的开源框架WebViewJav ...

  9. 【JavaScript】

    右键禁用.防止文字选中 .返回选中的文本 JavaScript 原理 Javascript高性能动画与页面渲染 前端不为人知的一面--前端冷知识集锦 屏幕外去计算值,position:absolute ...

随机推荐

  1. Unified Emoji表情for Android

    这个是我做Android以来碰到的最烦的东西,该死的emoji表情,恨之入骨..无奈这个问题分配给我了.我也只能硬着头皮做. 0.吐个槽先 首先,你要明白什么是emoji表情,不知道的google,不 ...

  2. 在Android里完美实现基站和WIFI定位

    来自:http://www.cnblogs.com/coffeegg/archive/2011/10/01/2197129.html 众所周知的,在OPhone和大部分国产的Android定制机里不支 ...

  3. 对Memcached使用的总结和使用场景

    1.memcached是什么 Memcached 常被用来加速应用程序的处理,在这里,我们将着重于介绍将它部署于应用程序和环境中的最佳实践.这包括应该存储或不应存储哪些.如何处理数据的灵活分布以 及如 ...

  4. IO多路复用的几种实现机制的分析

    http://blog.csdn.net/zhang_shuai_2011/article/details/7675797 select,poll,epoll都是IO多路复用的机制.所谓I/O多路复用 ...

  5. Drawable(5)关于从资源文件构造的Drawable不显示

    要给它设置个bounds才可以 TextView noticeHeaderView; TextView headerRefreshText; ProgressBar headerRefreshPgrs ...

  6. Cookie的具体使用之来存储对象

    1.创建一个新的cookie,并赋值. HttpCookie cookie;       cookie=new HttpCookie("user");       cookie.D ...

  7. Java开发之反射的使用

    通过类名获取类. Class serviceManager = Class.forName("android.os.ServiceManager"); 获取方法 Method me ...

  8. [HIHO1051]补提交卡(枚举,贪心)

    题目链接:http://hihocoder.com/problemset/problem/1051 思路:先排序,然后枚举连续的长度为m的子段,用这个段之后的第一个天数减去这个段之前的第一个天数再-1 ...

  9. MySQL学习笔记二

    Ø function 函数 函数的作用比较大,一般多用在select查询语句和where条件语句之后.按照函数返回的结果, 可以分为:多行函数和单行函数:所谓的单行函数就是将每条数据进行独立的计算,然 ...

  10. hdu 4952 Number Transformation (找规律)

    题目链接 题意:给你个x,k次操作,对于第i次操作是:要找个nx,使得nx是>=x的最小值,且能整除i,求k次操作后的数 分析: 经过打表找规律,会发现最后的x/i,这个倍数会趋于一个固定的值, ...