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. tomcat中如何运行war包呢

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPQAAADRCAIAAAB0LAgsAAAQtklEQVR4nO2d7W/bxh3H9ZfZbRrFOj

  2. 开源入侵检测系统OSSEC搭建之一:服务端安装

    OSSEC是一款开源的多平台的入侵检测系统,可以运行于Windows, Linux, OpenBSD/FreeBSD, 以及 MacOS等操作系统中.主要功能有日志分析.完整性检查.rootkit检测 ...

  3. CMake学习(1)---简单程序与库

    cmake是linux平台下重要的工具,可以方便的组织makefile.之前一直在windows平台下进行软件开发,在vs2010的IDE里,只要一点run程序就能跑出结果.但是程序的编译并没有那么简 ...

  4. (六)CSS伪元素

    CSS伪元素用于向某些选择器设置特殊效果. 伪元素的用法和伪类相似: selector:pseudo-element {property:value;} CSS类也可以与伪元素配合使用: select ...

  5. Linux文件与进程的Capability简介

    UID这种权限机制颗粒太粗,容易引起权利过剩(溢出),Linux引入了Capability:每个Capability系统内以一位Bit代表,OS内部使用64bit存储. 下面是android的capa ...

  6. 自动化:Appium运行成功,取得一个小的胜利

    看过乙醇大神的博文,然后又看了一些大神的博文,自己陆陆续续的折腾了一个月,今天上午的时候,appium终于跑起来了.纪念下,在自动化路上取得的一个小胜利 Appium版本:1.2 Python版本:2 ...

  7. MyBatis学习总结(5)——实现关联表查询

    一对一关联 提出需求 根据班级id查询班级信息(带老师的信息) 创建表和数据 创建一张教师表和班级表,假设一个老师负责教一个班,那么老师和班级之间的关系就是一对一的关系. create table t ...

  8. Android中使用Parcelable

    今天 在两个Activity之间传集合类型数据,看了一下,要用Parcelable 所以就看一下东西: 下面一段话是复制网友的. Android序列化对象主要有两种方法,实现Serializable接 ...

  9. Android远程图片获取和本地缓存

    对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对 应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用 ...

  10. 函数ut_2_log

    计算某个数的对数(最大的) 例如 16 计算后为 4 2的4次方为16 例如15 计算后为3 2的3次方为8 /******************************************** ...