面向对象的JavaScript-1.语言特征

来源:互联网 发布:js数组添加值 编辑:程序博客网 时间:2024/05/20 23:36

JavaScript语言最重要的几个部分:
* 引用(reference)
* 作用域(scope)
* 闭包(closure)
* 上下文(context)

1 引用

“引用”是一个指向对象实际位置的指针。
但是有一个前提:实际的对象肯定不会是引用。字符串永远是字符串,数组永远是数组。不过多个变量却能指向同一对象。
此外,对象可以包含一系列属性(property),这些属性也都不过是其他对象(字符串、数字、数组等待)的引用。如果多个变量指向同一个对象,那该对象的类型一改变,所有这些变量也会跟着响应改变。

多个变量引用同一个对象的例子

// 创建obj空对象var obj = {};// objRef 是另一个对象的引用var objRef = obj;// 修改原对象的一个属性obj.oneProperty = true;// 这个修改在两个变量中都反应出来了,因为他们引用的是同一个对象alert(obj.oneProperty === objRef.oneProperty) // true

自修改对象在JavaScript中很少见。

自修改对象的例子

var items = ['one', 'two', 'three']// 创建一个数组的引用var itemsRef = items;// 给原数组添加一个元素items.push('four');alert(items === itemsRef) // true

必须要记住的是,引用指向的只能是具体的对象,而不是另一个引用。JavaScript里的结果是沿着引用链一直上溯到原来那个对象。

修改对象的引用,同时保持完整性的例子

var items = ['one', 'two', 'three']// 将 itemsRef 置为 items 的引用var itemsRef = items;// 将items置为一个新的对象items = ['new', 'array']alert(items != itemsRef) // true

实际对象已经改变了,但原来指向它的引用仍然指向旧的对象。

修改对象而生成新对象例子

var item = 'test';// itemsRef 指向同一个字符串对象var itemRef = item;// 将新文本接在这个字符串后面// 注意:这会创建一个新对象,而非修改原对象item += 'ing';// item 和 itemsRef 的值不相等了,因为系的字符串对象已被创建alert(item != itemRef) // true

2 函数重载和类型检查

函数重载必须依赖两件事情:
1. 判断传入参数数量的能力
2. 判断传入参数类型的能力

参数数量

JavaScript的每个函数都带有一个仅在这个函数范围内作用的变量称为参数(argument),它是一个包含所有传给函数的参数的伪数组(pseudo-array),所以它并非真正意义的数组(也就是说你不能修改它,也不能用push()来添加新元素),但可以访问其中的元素,它也具有.length属性。

JavaScript中函数重载的两个例子

function sendMessage(msg, obj){  if ( arguments.length == 2 ) {    // 给对象发送消息    obj.handleMsg(msg);  } else {    alert(msg);  }}sendMessage ('hello world');// 传入对象,负责用另一套办法显式信息sendMessage('how are you', {  handleMsg: function (msg) {    alert('This is a custom message: ' + msg)  }})
// 一个接受任意数量参数并将其转化为数组的函数function makeArray () { var arr = []; for (var i=0; i< arguments.length; i++) {  arr.push(arguments[i]) } return arr;}console.log(makeArray(1,2,3,4,5)) // [1, 2, 3, 4, 5]

如果没有提供参数,它的类型必为undefined.

显示错误信息和默认信息的例子

function displayerror (msg) {    if ( typeof msg == 'undefined') {      msg = 'An error occurred.';    }    alert(msg)}displayerror ()

类型检查

typeof 是类型检查。既然JavaScript是动态类型的语言,有许多可以检查变量的类型。
1. 第一种使用typeof操作符
此方法只提供了一个字符串名称,用于表达变量的类型。
当变量不是object或array类型时,都能检测。但是无法区分Object和Array,它们都是返回object。

var obj={}var arr = [1,2,3,4]console.log(typeof obj) // objectconsole.log(typeof arr) // object


  1. 第二种检查对象类型的方法,需要引用所有JavaScript对象都带有的一个属性,称为构造函数。这一属性引用的是原本用来构造该对象的那个函数

使用构造函数属性来判断对象的类型的例子

var obj = {an: 'object'}var arr = ['an', 'array']var fn = function(){}var str = 'a string'var num = 55var boo = truefunction User() {}var newFn = new User()console.log(typeof(obj) + " : " + obj.constructor)// object : function Object() { [native code] }console.log(obj.constructor == Object) // trueconsole.log(typeof(arr) + " : " + arr.constructor)// object : function Array() { [native code] }console.log(arr.constructor == Array) // trueconsole.log(typeof(fn) + " : " + fn.constructor)// function : function Function() { [native code] }console.log(fn.constructor == Function) // trueconsole.log(typeof(num) + " : " + num.constructor)// number : function Number() { [native code] }console.log(num.constructor == Number) // trueconsole.log(typeof(boo) + " : " + boo.constructor)// boolean : function Boolean() { [native code] }console.log(boo.constructor == Boolean) // trueconsole.log(typeof(newFn) + " : " + newFn.constructor)// object : function User() {//  this.user = 'people'// }console.log(newFn.constructor == User) // true

3 作用域

在JavaScript里,作用域是由函数划分的,而不是由块划分的(如while,if 和for语句中间)。导致的结果是某些代码不好理解

var foo = 'test'if (true) {  var foo = 'new test'}function test (){  var foo = 'old test'  console.log(foo == 'old test') // true}test()console.log(foo == 'new test') // true

可见函数是有作用域的

JavaScript中全局作用域和window对象的例子

// 一个全局作用域下的变量,存储了字符串‘test'var foo = 'test'// 全局变量和window对象的foo属性是一致的console.log(window.foo == foo) // true

如果变量没有显式定义,它就是全局定义的。

function test (){  foo = 'new test'}test()console.log(foo == 'new test') // trueconsole.log(window.foo == 'new test') // true

4 闭包

闭包(closure)意味着内层的函数可以引用存在于包围它的函数内的变量,即使外层函数的执行已经终止。

闭包的例子

var obj = document.getElementById('main')obj.style.border = '1px solid red'// 初始化一个在1秒后执行的函数setTimeout(function(){    // 引用了全局变量obj   obj.style.display = 'none'},1000)function delayedAlert(msg, time){  setTimeout(function(){    // 它将使用包含本函数的外围函数传入的msg变量    alert(msg)  },time)}delayedAlert('welcome' , 2000)

闭包的额外作用。在函数式程序语言里,有一种称为Curry化的技术。
Curry化是一种通过把多个参数填充到函数体中,实现将函数转化为一个新的经过简化的函数的技术。

用闭包实现的函数Curry化的例子

 // 数字求和函数的函数生成器function addGenerator (num) {  //  返回一个简单的函数,求两个数字的和,其中第一个数字来自生成器  return function(toAdd) {    return num + toAdd  }}// addFive 现在包含一个接受单一参数的函数,这个函数能求得5加上该函数的和var addFive = addGenerator(5)// 传入参数求和console.log(addFive(4)) // 9

闭包的另一种使用方式:自执行的匿名函数。
自执行匿名函数可以把所有属于全局的变量都隐藏起来

使用匿名函数来隐藏全局作用域变量的例子

(function(){  var msg = 'Thanks for visiting'  window.onload = function(){    alert(msg)  }})()

在for循环中使用闭包,利用闭包来引用循环的计数器

var obj = document.getElementById('main')var items = ['click', 'keypress']// 使用一个自执行的匿名函数来激发出作用域for (var i = 0; i< items.length; i++) {  (function(){    // 记住在这个作用域内的值    var item = items[i]    将一个函数绑定到该元素    obj['on'+item] = function() {      // item 引用本for循环上下文所属作用域中的一个父变量      alert('Thanks for your ' + item)    }   })()}

5 上下文对象

上下文对象是通过this变量体现的,这个变量永远指向当前代码所处的对象中。即使在全局上下文中,this变量指向window对象。

在上下文对象内使用函数并将其上下文切换为另一个变量的例子

var obj = {  yes: function () {    // this == obj    this.val = true  },  no: function () {    this.val = false  }}// obj对象没有val属性console.log(obj.val == null) // true// 指向yes函数后,将val属性与obj对象关联起来obj.yes()console.log(obj.val == true) // true//window.no 指向obj.no并执行var no = obj.nono()// obj对象的val不变console.log(obj.val == true) // true// window的val属性被更新了console.log(val == false) // true

把obj.no变量的上下文切换为window变量时,代码变得不好理解
JavaScript提供了一套方法来让这一过程变得更好理解和实现

修改函数上下文对象的例子

<div id="main" style="width:100px; height:100px"></div><script>function changeBackgroud (color) {  this.style.background = color}// 在window对象中调用次函数会失败,因为window对象没有style属性// changeBackgroud('red')var main = document.getElementById('main')// changeBackgroud函数虽然在全局作用域下执行,但是把this指向了main// 使用call方法将它的颜色置为黑色。call方法将上下文对象设置为第一个参数,并将其他参数作为原函数的参数changeBackgroud.call(main,'black');// 设置body元素颜色的函数function setBodyColor () {  // apply 方法将上下文对象设置为第一个参数制定的body元素,第二个参数是传给函数的所有参数的数组  changeBackgroud.apply(document.body, arguments)}setBodyColor('red')</script>

原文来自精通JavaScript和Pro JavaScript Techniques