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

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) // object
console.log(typeof arr) // object


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

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

var obj = {an: 'object'}
var arr = ['an', 'array']
var fn = function(){}
var str = 'a string'
var num = 55
var boo = true
function User() {}
var newFn = new User()

console.log(typeof(obj) + " : " + obj.constructor)
// object : function Object() { [native code] }
console.log(obj.constructor == Object) // true

console.log(typeof(arr) + " : " + arr.constructor)
// object : function Array() { [native code] }
console.log(arr.constructor == Array) // true

console.log(typeof(fn) + " : " + fn.constructor)
// function : function Function() { [native code] }
console.log(fn.constructor == Function) // true

console.log(typeof(num) + " : " + num.constructor)
// number : function Number() { [native code] }
console.log(num.constructor == Number) // true

console.log(typeof(boo) + " : " + boo.constructor)
// boolean : function Boolean() { [native code] }
console.log(boo.constructor == Boolean) // true

console.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') // true

console.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.no
no()

// 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>

原文来自精通JavaScriptPro JavaScript Techniques

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值