重新介绍 JavaScript(JS 教程)摘要



JavaScript 中  null  和  undefined  是不同的,前者表示一个空值(non-value),必须使用null关键字才能访问,后者是“undefined(未定义)”类型的对象,表示一个未初始化的值,也就是还没有被分配的值。我们之后再具体讨论变量,但有一点可以先简单说明一下,JavaScript 允许声明变量但不对其赋值,一个未被赋值的变量就是  undefined  类型。还有一点需要说明的是, undefined  实际上是一个不允许修改的常量。


对象
JavaScript 中的对象可以简单理解成“名称-值”对,不难联想 JavaScript 中的对象与下面这些概念类似:
  • Python 中的字典
  • Perl 和 Ruby 中的散列(哈希)
  • C/C++ 中的散列表
  • Java 中的 HashMap
  • PHP 中的关联数组
这样的数据结构设计合理,能应付各类复杂需求,所以被各类编程语言广泛采用。正因为 JavaScript 中的一切(除了核心类型,core object)都是对象,所以 JavaScript 程序必然与大量的散列表查找操作有着千丝万缕的联系,而散列表擅长的正是高速查找。
“名称”部分是一个 JavaScript 字符串,“值”部分可以是任何 JavaScript 的数据类型——包括对象。这使用户可以根据具体需求,创建出相当复杂的数据结构。
有两种简单方法可以创建一个空对象:
var obj = new Object ();
和:
var obj = {};
这两种方法在语义上是相同的。第二种更方便的方法叫作“对象字面量(object literal)”法。这种也是 JSON 格式的核心语法,一般我们优先选择第二种方法。


学习 JavaScript 最重要的就是要理解对象和函数两个部分。最简单的函数就像下面这个这么简单:
function add ( x , y ) { var total = x + y ; return total ; }
这个例子包括你需要了解的关于基本函数的所有部分。一个 JavaScript 函数可以包含 0 个或多个已命名的变量。函数体中的表达式数量也没有限制。你可以声明函数自己的局部变量。 return 语句在返回一个值并结束函数。如果没有使用  return  语句,或者一个没有值的  return  语句,JavaScript 会返回  undefined


关键字  this 。当使用在函数中时, this  指代当前的对象,也就是调用了函数的对象。如果在一个对象上使用 点或者方括号 来访问属性或方法,这个对象就成了  this 。如果并没有使用“点”运算符调用某个对象,那么  this  将指向全局对象(global object)。
我们引入了另外一个关键字: new ,它和  this  密切相关。它的作用是创建一个崭新的空对象,然后使用指向那个对象的  this  调用特定的函数。注意,含有  this  的特定函数不会返回任何值,只会修改  this  对象本身。 new  关键字将生成的  this  对象返回给调用方,而被  new  调用的函数成为构造函数。习惯的做法是将这些函数的首字母大写,这样用  new  调用他们的时候就容易识别了。
Person.prototype  是一个可以被 Person 的所有实例共享的对象。它是一个名叫原型链(prototype chain)的查询链的一部分:当你试图访问一个  Person  没有定义的属性时,解释器会首先检查这个  Person.prototype  来判断是否存在这样一个属性。所以,任何分配给  Person.prototype  的东西对通过  this  对象构造的实例都是可用的。
原型组成链的一部分。那条链的根节点是  Object.prototype ,它包括  toString()  方法——将对象转换成字符串时调用的方法。


闭包
下面我们将看到的是 JavaScript 中必须提到的功能最强大的抽象概念之一:闭包。但它可能也会带来一些潜在的困惑。那它究竟是做什么的呢?
function makeAdder ( a ) { return function ( b ) { return a + b ; } } var x = makeAdder ( 5 ); var y = makeAdder ( 20 ); x ( 6 ); // ? y ( 7 ); // ?
makeAdder  这个名字本身应该能说明函数是用来做什么的:它创建了一个新的  adder  函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加在外层函数传进来的参数上。
这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以访问外部函数的变量。唯一的不同是,外部函数被返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在——否则  adder  函数将不能工作。也就是说,这里存在  makeAdder  的局部变量的两个不同的“副本”——一个是  a  等于5,另一个是  a  等于20。那些函数的运行结果就如下所示:
x ( 6 ); // 返回 11 y ( 7 ); // 返回 27
下面来说说到底发生了什么。每当 JavaScript 执行一个函数时,都会创建一个作用域对象(scope object),用来保存在这个函数中创建的局部变量。它和被传入函数的变量一起被初始化。这与那些保存的所有全局变量和函数的全局对象(global object)类似,但仍有一些很重要的区别,第一,每次函数被执行的时候,就会创建一个新的,特定的作用域对象;第二,与全局对象(在浏览器里面是当做  window  对象来访问的)不同的是,你不能从 JavaScript 代码中直接访问作用域对象,也没有可以遍历当前的作用域对象里面属性的方法。
所以当调用  makeAdder  时,解释器创建了一个作用域对象,它带有一个属性: a ,这个属性被当作参数传入  makeAdder  函数。然后  makeAdder  返回一个新创建的函数。通常 JavaScript 的垃圾回收器会在这时回收  makeAdder  创建的作用域对象,但是返回的函数却保留一个指向那个作用域对象的引用。结果是这个作用域对象不会被垃圾回收器回收,直到指向  makeAdder  返回的那个函数对象的引用计数为零。
作用域对象组成了一个名为作用域链(scope chain)的链。它类似于原型(prototype)链一样,被 JavaScript 的对象系统使用。
一个 闭包 就是一个函数和被创建的函数中的作用域对象的组合。


prototype和Object.getPrototypeOf
对于从 Java 或 C++ 转过来的开发人员来说 JavaScript 会有点让人困惑,因为它全部都是动态的,都是运行时,而且不存在类(classes)。所有的都是实例(对象)。即使我们模拟出的 “类(classes)”,也只是一个函数对象。
你可能已经注意到我们的 function A 有一个叫做 prototype 的特殊属性。该特殊属性可与 JavaScript 的 new 操作符一起使用。对原型对象的引用被复制到新实例的内部 [[Prototype]] 属性。例如,当执行 var a1 = new A() 时,JavaScript(在内存中创建对象之前,并且在运行函数 A() 之前)定义了它)设置 a1.[[Prototype]] = A.prototype 。然后当您访问实例的属性时,JavaScript首先会检查它们是否直接存在于该对象上,如果不存在,则会 [[Prototype]] 中查找。这意味着你在 prototype 中定义的所有内容都可以由所有实例有效共享,你甚至可以稍后更改部分 prototype ,并在所有现有实例中显示更改(如果需要)。
像上面的例子中,如果你执行 var a1 = new A(); var a2 = new A(); 那么 a1.doSomething 事实上会指向 Object.getPrototypeOf(a1).doSomething ,它就是你在  A.prototype.doSomething  中定义的内容。比如: Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething
简而言之,  prototype 是用于类型的,而  Object.getPrototypeOf()  是用于实例的(instances),两者功能一致。
[[Prototype]]  看起来就像 递归 引用, 如 a1.doSomething Object.getPrototypeOf(a1).doSomething Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething  等等等, 直到它被找到或 Object.getPrototypeOf 返回 null。
因此,当你执行:
var o = new Foo ();
JavaScript 实际上执行的是:
var o = new Object (); o . __proto__ = Foo . prototype ; Foo . call ( o );
(或者类似上面这样的),然后当你执行:
o . someProp ;
它检查 o 是否具有 someProp 属性。如果没有,它会查找  Object.getPrototypeOf(o).someProp ,如果仍旧没有,它会继续查找  Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp

猜你喜欢

转载自blog.csdn.net/taoqilin/article/details/79516345
今日推荐