javascript面向对象——语言特征

引用:
引用就是指向对象实际位置的指针,但是前提是,实际的对象决不是一个引用:字符串总是一个字符串,数组总是一个数组。然而,多个变量可以引用相同的对象。
对象能包括一系列的属性,这些属性简单地引用其它对象(如字符串,数字,数组等等)。
特点:
当几个变量指向相同的对象时,我们只要修改底层对象类型就能够在所有的指向它的变量上有所反映。
例如:
var obj = new Object();	//创建一个新的对象,注意创建新对象的方法。
var objRef = obj;
obj.oneProperty = true;
alert(obj.oneProperty === objRef.oneProperty);	//true
数组的push方法:
数组能够用push方法给它自己增加额外的项。因为在数组对象的核心值是作为对象的属性储存的。
注意:
1、这里有两个小知识点 push 方法是在 数组 中使用的,
2、数组对象的值是作为对象的属性储存的。(在数组中储存的值即可为属性)
例如:
var items = new Array("one","two","three");
var itemsRef = items;
items.push("four");
alert(items.length === itemsRef.length);	//true
总结:我们应该始终记住一点,引用总是只指向最终被引用的对象,而不会是引用本身。比如上面(itenRef在实例中引用的是谁,从表象上看他是引用items,这点是没有错的,但是我们也能看见的是items引用的是一个新建的数组对象,这时我们使用push方法的在原数组对象上追加了一个新的值,而弹出的结果为true,这就说明一点:一个引用指向另一个也是引用的变量。但在 JavaScript 里,它会沿着引用链向下追溯直到指向核心的对象。物理的目标虽然已经改变但是引用仍然指向原来的对象。)
还有另外一种情况需要我们注意的是:
代码如下:
var items = new Array("one","two","three");
var itemsRef = items;
alert(items === itemsRef);	//true
items = new Array("new","array");
alert(items === itemsRef);	//false
有没有发现有什么不一样?itemsRef指向的是第一个数组所以第一次弹出的会是true,但是当我们在为items重新创建新的数组的时候item的引用对象就会被替换,因此第二个弹出的结果就变成了false了。
下面我们来看一个有些奇怪的例子:表面上看似乎是一个自修改的对象,却作用于一个新的未被引用的对象,当我们执行字符串的串联时,结果总是一个新的字符串对象,而不是原来字符串更改后的版本。
代码如下:
var item = "test";
var itemsRef = item;
console.info(item);	//test
item += "ing";	//进行字符串的串联
console.info(item);	//testing
console.info(itemsRef);	//test
console.info(item === itemsRef);	//false
从上面代码部分你能看出什么?是的,我们在进行字符串串联的时候其结果是一个新的字符串对象,而不是在是原来字符串更改后的版本,也就是说,原来的item变量所引用的字符串还依旧存在,当我们执行了串联后,原来的字符串依旧不变,而是有多出来了一个新的字符串对象,所以最终结果就变成了false。
函数重载和类型检查:
函数重载:
javascript中函数重载有一个很重要的变量名为arguments,根据传给函数不同数目或类型的参数,从而使函数执行不同的操作。
什么是arguments:它的行为类似于一个伪数组,包含了传给函数的所有参数,参数不是一个真正的数组(这就意味着我们不能修改它,或者调用push()方法增加新的项),但是我们可以用数组的形式来访问它,而且它也的确是有length属性的。
我们来看看下面的几个实例:
实例一:
function myfile(name,work){
    if(arguments.length == 2){
        alert("这是正确的");
    }else{
        alert("这是错误的");
    }
};
myfile("zhang","IT");		//这是正确的。
当我们运行函数myfile()的时候向函数传入两个参数,根据函数体中的条件语句来进行判断,弹出来的的确是 “这是正确的” 的信息。
function myfile(){
    var arr = [];
    for(var i=0;i<arguments.length;i++){
        arr.push(arguments[i]);
    }
    return arr;
}
var myfile1 = myfile("one","two","three","four","five");
alert(myfile1);	//one two three four five
在上面的实例中显示的则是更灵活,我们无需限制参数的数目可以对参数进行操作,用return返回结果,从而做到无论参数的数目有多少我们都能在实例中进行操作。
另外我们还有一种另外一种方式来判断传给一个函数的参数数目的方法。
实例代码如下:
function myfile(name){
    if (typeof name === 'undefined'){
        name = "没有传入参数";
    }
    alert(name);
}
myfile();
在上面的代码中我们并没有传入任何参数,因此当我们运行myfile()函数时弹出的是 “没有传入参数”那么如果我们传入一个参数呢!就像这样 myfile("zhang");我们会发现这时弹出的就是 “zhang”,这就是当参数缺省的时候,也让它弹出一个错误的信息,告诉我们,我们在运行的时候并没有传入任何的参数。
类型检查:
typeof操作符(我们可以使用typeof来检查对象的数据类型)
其实typeof操作符就是一种可以检查对象的数据类型的操作符,typeof本身并不复杂,但是它却可以用来实现复杂的方法,给开发者和我们的代码的使用者提供更好的体验。
例如:
function myfile(num){
    if (typeof num == "string"){
        num = parseInt(num);
    }
    return alert(typeof num);
}
myfile("10");
在浏览器中弹出的结果为 number;因为很多时候在实际项目中我们需要知道对象的类型,从而进行类型之间的转换,以便更好的去操作对象,这时typeof就比较重要。
constructor属性
其实在javascript中能够检查对象的数据类型不仅仅只有typeof操作符,constructor构造函数属性也是检查对象数据类型方式的一种。
例如:
在我们正式介绍constructor构造函数属性之前先来看看下面的这个小实例:
var num1 = "8";
var num2 = 2;
var num3 = num1 + num2;
alert(typeof num3); 
如上,我们看见num1是属于string类型,num2是属于number类型,那么在上面的代码中num3的数据类型又是什么样的呢!?我们通过实验得知num3的结果是“82”数据类型是string字符串类型。
现在我们来看看使用constructor构造函数属性的方法的实例代码:
function myfile(num){
    if (num.constructor == String){
        num = parseInt(num);
    }
    return num;
}
var myfile1 = myfile("8");
alert(myfile1+=2);
alert(typeof(myfile1+=2));
通过运行上面的代码我们得到的结果是10数据类型是number,那么我们是不是就能够确定constructor构造函数也能检查数据的类型呢!?当然答案是肯定的。只是打印出的结果两者有所不同。如下代码所示。
var num = 10;
console.info(num.constructor);
console.info(typeof num);

由上图可见虽然两种方式都能够输出正确的结果,但是很显然constructor构造函数属性输出的结果更具体一些,但是typeof操作符输出的结构就仅仅只是数据的类型。
另外二者的书写方式是有很大区别的如: typeof num == "string" num.constructor == String是不是看出了不同,所以以后我们在开发中如果遇到需要使用这两者中的一种或两种时一定要注意其中的细节部分。
作用域:
作用域是JavaScript中一个比较难处理的特性所有的面向对象的编程语言都有某种形式的作用域;主要看是什么上下文约束着作用域。在 JavaScript 里,作用域由函数约束,而不由块约束(如 while,if,和 for 里的语句体)。最终可能使得一些代码的运行结果表面上显得怪异(如果你来自一种块作用域语言的话)。
我们来设置一个等于"zhang"的全局变量name
通常的写法如下:
var name = "zhang";
alert(name);	//zhang
但是如果我们是定义在块中的呢!?
if (true){
	var name = "new zhang";
}
alert(name);	//new zhang
其实上面定义在块中的变量也是全局变量,但是我们却很少这样写,这就是上面说的表面上显得比较怪异。
那么我们在一个函数体中来定义呢!?如下代码:
function myfile(){
var name = "old zhang";
}
myfile();
alert(name);
这个时候我们发现在浏览器中什么都没有显示,这个就是作用域是会受到函数的约束。
还有一种情况就是当一个变量遗漏定义的时候会出现什么情况,所谓的遗漏就是给变量赋值但是却并没有定义它,相当于我给变量 name 赋值 zhang 但是却没有用 var 来定义它,我们看如下代码:
function myfile(){
name = "zhang";
}
myfile();
console.info(name);
我们可以看见结果如下图:

是的,我们在遗漏定义的时候确实是可以作用在全局当中的。
总结:在函数中当一个变量没有被明确定义的时候,只是为其赋值,那么它将成为全局变量,即使它只在函数的上下文中使用。
提示:我们应该注意的是其实所有的全局变量实际上它都是window对象的属性。比如上面代码中的实例:我们也可以这样写alert(name);——alert(window.name);
闭包:
闭包的作用:在闭包中内层的函数可以引用存在于包绕它的函数的变量,即使外层的函数的执行已经终止。
下面我们用闭包的概念来封装一个定时隐藏的div框,具体代码如下:
<div id="div1" style="width:300px;height:100px;"></div>
<script>
    var oDiv = document.getElementById("div1");
    oDiv.style.border = "1px solid red";
    function myfile(name,time){
        setTimeout(function(){
            name.style.display = "none";
        },time);
    }
    myfile(oDiv,2000);
使用闭包完全可以把混乱的代码清理掉,上面这个例子比较简单,有一个回调函数在调用setTimeout函数然后以2秒后被调用,而它仍然引用了变量oDiv(定义在全局范围,指向id为“div1”的元素)。而myfile函数则展示了解决setTimeout出现混乱的方法,以及函数作用域内可以有闭包的能力。
但是在下面的这个实例中,我们理解起来可能就不会有那么简单:
function myfile(oneNum){
return function addFile(twoNum){
return oneNum + twoNum;
}
}
var myFile1 = myfile(8);
alert(myFile1);
alert(myFile1(2));
这个实例中内部函数addFile()调用了外部函数myfile()函数的参数,最终弹出的结构是10,可能有些人不是很理解为什么会弹出结果为10呢!?
我们来看看弹出的东西都有什么,这时候我们就很清楚为什么结果会是10.
第一次弹出的结果为如下图:

其实我们可以将上面的代码这样重写:
var myFile1 = myfile(8);	//其实就相当于下面这段代码
myFile1 = function addFile(twoNum){
return oneNum + towNum;
}
当第一次给myfile传入参数8是,我们就将数字8赋值给了oneNum,这时当我们向myFile1中传入参数2的时候,正好运行的结果就是return oneNum + twoNum;所以这就是为什么其结果为10的原因,同时它也是一个典型的闭包函数的实例,内部函数引用外部函数的参数。
在闭包中,还引用了一个匿名函数的概念,什么是匿名函数?匿名函数有什么好处呢!?
匿名函数:顾名思义,我们经常在写函数的时候,都会为其命名,方便调用,但是匿名函数是没有名称的。
其中匿名函数,this指向问题,new运算符与实例化对象的不同点我会分开讲解,因为比较重要,理解起来有些麻烦。

老铁们,欢迎对我的理解批评指正,我会虚心接受每一位老铁给我提供的建议或意见,我们一起共同进步啊!!!!谢谢!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值