《JavaScript高级程序》学习总结四(1)
引言:这一章开始我们进入变量、作用域内存的重要总结,对我来说 JavaScript的总结也开始步入“正轨”了,因为前面几章的知识点其实对学习过编程,对js有了解的都不会觉的困难。而这一章开始包括后面的几章,需要理解的概念性的东西会越来越多,从中我们可以了解到JavaScript这门语言的特点包括这门语言的逻辑思想也是越来越多。我觉得学习一门编程语言,单单会用是不够的,每一门语言的设计里面都透露着这门语言的逻辑性,JavaScript更是如此,作为一门脚本语言,刚开始出来经历“曲折”的发展,之后又如何开始变得规范等等,这一系列都让这门语言透露着与别的语言似乎相关但是又与众不同。特别是其作用域、闭包、原型链与继承等等,让人不得不靠近去思考。(很巧妙不是么,JavaScript的设计者能够想出这样的方式去实现)。同样这一章也是我感觉学习 JavaScript 困难的开始,而且在学习期间了解到这些知识可能在“严格模式”下又有另一番风景,后面更有 ES6在等待我们,这想想都让人头大,但是这些困难也是为了让我们迎接新的高度。所以,让我们开始吧。
基本数据类型与引用数据类型:
ECMAScript 变量可能包含两种不同类型的值:基本数据类型 与 引用数据类型。
基本数据类型:undefined 、null 、String 、number 、boolean。这五种基本数据类型是按值访问的,因为可以操作保存到变量中的实际的值。
引用类型:引用类型的值是保存到内存中的对象。引用类型的值是按引用访问的,因为 JavaScript 不允许直接访问内存中的位置(也就是说不能直接操作对象的内存空间),在操作对象的同时,实际上就是在操作对象的引用而不是实际对象。
PS:在很多语言中,字符串以对象形式来表示,因此被认为是引用类型,而 ECMAScript 放弃了这一传统。所以前面章节学习也有提到:ECMASCript 中字符串是不可改变的,也就是说字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个字符串来填充该变量。(在别的语言中,因为字符串是对象,引用类型,所以改变一个字符串其实只要将引用指向新的字符串即可,不需要立刻的销毁原来的字符串)。
动态属性:定义基本数据类型与对象的方式是类似的:创建一个变量并为其赋值。但是,当这个值保存到变量中以后,对不同类型值可以执行的操作则大相径庭。person
比如:var person = new object();
person.name="jack";
alert( person.name) //jack 这是创建一个对象,并给对象添加一个name 属性,并给这个属性赋值 jack .
------------------------------------------------------------------------------------------------------------------------------------------------------------
var name="jack";
name.age=18;
alert( name.age)//undefined 这里我们给一个基本数据类型添加属性,尽管不报错,但是很显然并不能与引用类型那般可以动态添加属性。这也是二者的区别。
(后面我们会总结到 基本包装类型,而包装类型是特殊的引用类型,总结到那里后很容易会对这个知识点有更深的理解。所以我这里也提示下,接下来的包装类要看仔细了(基本包装类知识点会在 总结五)。)
复制变量值:
基本数据类型与引用类型对变量的复制也是不同。直接上例子:
基本数据类型:
var num1=5;
vae num2=num1; // 在此,num1中保存的值是5 ,当使用 num1 的值来初始化num2 ,那么num2 中也保存了 数值 5,但是,num1 与 num2 中的值是完全独立的,此后二者之间的值改变都不会影响对方。
引用数据类型:
var obj1=new object();
var obj2=new obj1;
obj1.name="jack";
alert( obj2.name)// jack 在此,变量 obj 1保存了一个对象的新实例,这个值被复制到了 obj 2 中,换句话说 obj1 与 obj2 都指向同一个对象,这样当 obj1 的属性改变时,obj2 也会受到影响。
传递参数:
ECMAScript 中所有的函数的参数都是按值传递的。也就是说把函数外部的值复制给函数内部的参数,就跟把值从一个变量复制给另一个变量一样。有很多小伙伴看到这里可能懵逼了(包括我第一次看到这里也是),因为访问变量有按值和按引用两种方式,所以一提到参数,函数,大家可能就会想到按引用方式,但是实际上参数只能按值传递。
举个栗子:
function addNum(){
num+=10;
return num;
}
var count =20;
var result =addNum( count );
alert( count )// 20 无变化
alert ( result ) //30
假如 num 是按照引用传递的话,那么变量count 的值也应该是30 ,从而反映 函数内部的修改。
再举个栗子:
function setName ( obj){
obj.name="jack";
obj=new object ();
obj.name="tom";
}
var person=new object();
setName(person );
alert( person.name) //jack
在这里,我们在函数内重新定义了一个对象,并且重新定义了一个不同值的name 属性,在把 person 传递给setNam()后,其name 的属性被设置 “jack”, 然后又将一个新对象赋给变量 obj ,同时将其name 设置为 “tom ”,如果person 是按照引用传递的话,那么person 就会自动修改为指向其 name 属性值为 "tom" 的新对象,但是接下来访问时,person.name 却还是 jack 。这说明,即使在函数内部修改的值,但原始的引用依旧保持不变,实际上当在函数内部重写 obj 时,这一个变量引用的就是另一个局部对象了(我们可以在函数里最后添加 “alert( obj.name)” 与alert( person.name)就能看出了),而这个局部对象会在函数执行完毕后销毁。
instanceof:
我们前面已经学过了 typeof,这个方法能帮助我们判断一个变量是不是基本数据类型,而当我们采用其来判断 对象或者 null(这也是一个对象,前面提到过null 用来表示对象为空)。这时,typeof 返回的就是object ,这个时候 typeof 在某方面就有些力不从心了,比如我们想要知道一个对象的类型的时候,用typeof 是不能判断出来的,这时 我们可以采用 instanceof,
instance 的作用就是:我们可以通过这个操作符知道一个变量是否是给定的引用类型的实例。比如:alert ( person instanceof Object); //变量person 是 Object 吗?
根据规定,所有的 引用类型的值都是Object 的实例,所以采用instanceof 对其进行检验时,就都返回 true 。当然 用instanceof 判断基本数据类型时都返回false ,因为基本数据类型不是对象。
---------------------------------------------------------------------------------------------------------
本节完。下节预告:执行环境与作用域,及其内存。