JavaScript基础内容

JavaScript基础内容

1. 词法结构


(1). 字符集


Javascript程序使用UTF-16编码的Unicode字符集编写。Unicode是ASCII和Latin-1的超集。

(2). 大小写


Javascript区分大小写,html不区分大小写,xhtml区分大小写。

在html中标签和属性名可以使用大写,也可以小写,而在Javascript中必须是小写。

请看下面的例子: html中无论方法名都会执行。

可以看到在js中不是小写的方法根本没有执行。

(3)直接量


程序中直接使用的数据值;五大基本数据类型和数组初始化表达式,对象初始化表达式
a: 数字直接量:123
b: 字符串直接量:"hello world"
c: 布尔直接量:true
d: null
e: undefined
f: 数组直接量:数组初始化表达式 [1,2,3]
g: 对象直接量:对象初始化表达式 {a:1, b:2}

(4)标识符和保留字


Javascript中,标识符用来对变量和函数名进行命名或者用做JS代码中某些循环语句中的跳转位置的标记。
标识符命名规则:
a.必须以字母、下划线(_)或美元符($)开始。
b.后续的字符可以是字母、数字、下划线或美元符(数字是不允许作为首字符出现的,以便JS可以轻便区分开标识符和数字)。
c.出于可移植性和易于书写的考虑,通常只使用ASCII字母和数字来书写标识符。然而需要注意的是,JS允许标识符中出现Unicode字符全集中的字母和数字。由此,程序员也可以使用非英语语言或数学符号来书写标识符。
保留字:javascript把一些标识符拿出来用作自己的关键字,对于保留字其实不少。

(5)严格模式下同样对下面的标识符的使用进行了严格限制,它们并不是保留字,但不能用作变量名,函数名,参数名

arguments eval
还有javascript预定义的全局变量和函数

(6)自动填补分号


a. 如果语句通常独占一行,通常可以省略语句之间的分号。
b. 只有在缺少了分号就无法正确解析代码的时候,javascript就会自动填补分号。
c. return, break和continue遇到换行会自动填补分号
d. ++, --运算符如果作为后缀表达式的时候,操作数遇到换行就会会自动填补分号
x++; 如果写成
x
++
y
会被解析成x; ++y

2.类型, 值和变量


(1). Javascript的数据类型分为原始数据类型和对象类型


原始数据类型:数字,字符串,布尔值,null,undefined

(2). Javascript解释器有自己的内存管理机制,可以自动对内存进行垃圾回收


我们不用担心对象创建之后的回收问题。

(3). 包装对象


var s = 'string'; //这是一个字符串直接量
s.length          
这个时候通过调用new String(s)的方式转换成对象。引用完成之后,这个对象就会销毁(在实现上并不一定创建或销毁这个临时对象,看起来是这样)[这段解释是Javascript权威指南上的解释]。
note: 内存分为栈区(stack)和堆区(heap),然后在JS中开发人员并不能直接操作堆区,堆区数据由JS引擎操作完成.
变量标号,和基本数据直接量存放在栈区,对象的堆地址存放在栈区,对象存放在堆区

a. 包装对象转换成原始值:toString()和valueof()
b. Number(), String(), Boolean()当不通过new运算符操作时,会作为类型转换函数进行类型转换

c. Number类定义的toString()方法可以接受表示转换基数的可选参数,默认为10,可以说2, 8, 16。


(4). 变量声明:


a. 变量声明提前:
javascript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的,这个特性称为声明提前。这步操作实在javascript引擎“预编译”时进行的。

变量:

使用在声明之前,在当前的作用域下可以词法查询到该变量,只是还没有执行赋值的操作,例子: var a = 10; 编译的时候只是把var a;提前到了作用域的前端,具体的赋值操作是需要等到js执行的时候才会去做。
函数:
函数声明语句(function test() {}),函数的声明和函数体提前。使用函数声明语句创建的变量不能删除的,但不是不可变的,变量值可以重写。
var test1 = function() {};  函数的声明提前,但是函数体还是在原来的位置。
请看下图示例;调用了方法TestVariableDeclare(),并且输出了a的值,undefined,但是没有能成功调用TestVariableDeclare1().
                           JavaScript基础内容_第1张图片
b. 作用域以及作用域链会在后面会有单独的章节说明。


(5). Javascript不区分整数值和浮点数值,所有的数字均是由浮点数值表示


(6). NaN非数字值,它和任何值不相同,包括自身也不相同。


NaN !== Nan // true

(7). 任意的javascript的值都可以装换为布尔值


undefined, null, 0, -0, NaN, '', "" 装换为false

(8). Number类定义的toString()方法可以接受表示转换基数的可选参数


var number = 12;
number.toString(2); //"1100"

(9). undefined null


undefined表示系统级别的出乎意料的错误的值得空失。

null表示程序级别的,正常的,意料之中的值得空缺。


3. 运算符


(1). in:判断对象是否拥有某一个属性,如果是的,则会返回true,否则就会返回false。


property in object
左操作数是一个字符串或可以转换为字符串,右操作数是一个对象 

      JavaScript基础内容_第2张图片

(2). instanceof


希望左操作数是一个对象,右操作数标识对象的类,如果左侧的对象是右侧类的实例,则会返回true。需要注意的是当右边操作数不是函数,则会抛出类型错误异常,如下图。

                 JavaScript基础内容_第3张图片
Note: instanceof运算符的工作建立在原型链上的,后面会有专门的章节说明。

(3). typeof:一元运算符,放在操作数的前面,返回值为表示操作数类型的一个字符          

                  JavaScript基础内容_第4张图片
typeof 可以加上圆括号,可以让typeof看起来是一个函数名
typeof(123); //number

(4). delete: 一元操作符,用来删除对象属性或者数组对象元素,操作数是左值。


a). delete的操作存在返回值true或者false,delete对象的自由属性可以删除成功,并且返回true,delete删除不了内置核心属性和客户端属性,返回值也是true。请看下图示例:

可以从下图中看到book的name属性删除成功,但是继承的toString属性没有删除成功,但是返回值都是true,只是在删除toString的时候没有做任何操作。

              JavaScript基础内容_第5张图片

b. 当我们删除不可配置的属性的时候会返回false,如下图:JavaScript基础内容_第6张图片

c. 通过var语句声明的变量不能删除,通过function语句定义的函数和函数内部的参数也不能删除,从下图可以看到function testdeleteInFunction中a删除成功,所以log函数输出的时候报错了。我们也可以看到的是无论在函数中还是全局作用域中是相同的行为。delete通过var创建的变量会返回false。

                  JavaScript基础内容_第7张图片

(5). void: 一元操作符,出现在操作数之前,操作数可以是任意类型。操作数会照常进行,但是会忽略计算结果并返回undefined。


a. 为什么要用void?
因为undefined在javascript中不是保留字。

           JavaScript基础内容_第8张图片
如上图所示:可以再一个函数的上下文对undefined作为变量名并且赋值,但是在全局作用域中undefined的值不会改变。所以在函数上下文对于undefined的取值就不是完全的可靠,于是采用void的方式得到undefined。

b.  javascript: url中使用,例如
Test void 这种情况下link的点击不会刷新一次页面。

(6).  ,逗号运算符,先计算左侧表达式的值,在计算右侧表达式的值,然后返回右侧表达式的值。


4. 语句


(1). for in 

语句循环的是可枚举的属性,代码中定义的属性和方法都是可枚举的。javascript语言核心所定义的内置方法就不会被枚举,例如toString方法。

(2). with语句用来临时扩展作用域链。


(3). debugger语句在程序调试可用的情况下就是加上一个断点,什么也不做。


5. 对象


(1). 创建对象


a. 对象直接量
b. 通过new的方式创建对象
c. Object.create() 是一个静态的方法,而不是提供给某个对象调用的方法。 一个参数是新建对象的原型,第二个为可选参数,用以对对象的属性进行说明。


(2). 属性的查询和设置


a. object.property / object["proprtty"], []方式使用数字也是常见的。
b. 属性访问错误
访问属性不存在的时候会返回undefined,如果连访问的对象都不在的时候,再去访问对象上的属性就会报typeerror。如下图所示:

JavaScript基础内容_第9张图片
因为null与undefine是没有属性的。
所以当我们对于属性的存在不确定的时候就可以使用下面的方式
var toString = book && book.length && book.length.toString();

note: 内置构造函数的原型是只读的,如下图,在非严格模式下不会报错,只是赋值的操作不成功,但是在ECMASCRIPT 5的严格模式下会报错,如下图所示:
JavaScript基础内容_第10张图片

(3). 删除属性


delete可以删除对象的属性。它的操作数应当是一个属性访问表达式。需要注意的是delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性。

delete object["property"] 
delete object.property
上面delete操作符有对应的解释。
delete可以删除数组元素形成稀疏数组

(4). 检测属性:


js中的对象可以看做属性的集合,我们经常会判断属性是否存在于某个对象中

a. hasOwnProperty
  判断一个属性是定义在对象本身还是继承自原型链。自身对象返回true,继承属性返回false。


  note: Javascript中唯一一个处理对象属性而不会往上遍历原型链的。

b. propertyIsEnumable
  是hasOwnProperty()的增强版,只有检测是自有属性的可枚举性,如果是自有属性并且可枚举的返回true。

c. in上面章节已经提到过:

d. 判断属性值是不是undefined,可以使用 !==, 但是这个不能够区分不存在的属性和属性值为undefined的属性,还是需要用in来检测是不是存在属性

(5). 枚举属性


对象继承的内置方法是不可枚举的,但在对象中添加的属性是可枚举的,除非定义的时候是不可枚举的属性,或者转换为不可枚举的属性。
a. 枚举属性的特点:
1): 在对对象进行for in 遍历的时候可以被获取到。
2): Object.keys() 获取自有属性中枚举属性的合集字符串
3): JSON.stringify 自有属性枚举属性才会被JSON序列化。

b. 例子: 
var testEnumableParent = {
	'name': 'parent', 
	'value': 'Test Enumable Parent',
	'group': 'enumable'
}


Object.defineProperty(testEnumableParent, 'DisEnumableProperty', {
	value: 'dis enumable property',
	enumerable: false,
	writable: true,
	configurable: true
});


Object.defineProperty(testEnumableParent, 'EnumableProperty', {
	value: 'enumable property',
	enumerable: true,
	writable: true,
	configurable: true
});


var testEnumableChild = Object.create(testEnumableParent, {
	'name': {
		value: 'child',
		enumerable: true,
		writable: true,
		configurable: true
	},
	'value': {
		value: 'Test Enumable child',
		enumerable: true,
		writable: true,
		configurable: true
	},
	'related': {
		value: "I am the part of Object, I can't tell other, So i am not enumerable",
		enumerable: false,
		writable: true,
		configurable: true
	}
});

var enumablePropertyArray = [];

for (var prop in testEnumableChild) {
	enumablePropertyArray.push(prop);
}

console.log('For in 得到的结果: ' + enumablePropertyArray.toString());
console.log('Object.keys 的结果: ' + Object.keys(testEnumableChild));
console.log('JSON.stringify 的结果: ' + JSON.stringify(testEnumableChild));
/*得到的结果*/
For in 得到的结果: name,value,group,EnumableProperty
Object.keys 的结果: name,value
JSON.stringify 的结果: {"name":"child","value":"Test Enumable child"}

b. Object.getOwnPropertyNames();返回对象的所有自有属性的名称,而不仅仅是可枚举的属性。


(6) 属性getter和setter


属性值可以使用一个或两个方法进行代替,这两个方法就是getter和setter。

a. 普通属性(不是通过Object.defineProperty()创建)的属性特性
[[configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由数据属性修改为访问器属性
[[enumerable]] :默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
[[writable]] :默认值为true,表示能否修改属性的值,这是与[[Configurable]]不同之处。
[[value]] :默认值为undefined,这个值即为属性的属性值,我们可以在这个位置上读取属性值,也可以在这个位置上写入属性值。

b. Object.defineProperty()创建属性的默认值:
[[configurable]]:默认值为false. 设置为false之后,就不能再次对属性进行设置,否则会报错,
[[enumerable]] :默认值为false.
[[writable]] :默认值为false.
[[value]] :默认值为undefined.
下图示例:



note: 上图所示configurable初始化为false的时候,后面就就不能再更改为true。一旦修改就会出错。

c. 存取器属性:
[[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由访问器属性修改为数据属性
[[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
[[Get]]:在读取属性时调用的函数。默认值为undefined  关键:特性可以是一个函数
[[Set]]:  在写入属性时调用的函数。默认值为undefined 关键:特性可以是一个函数 

由于get和set函数也属于属性的特性,那么他们就有可能(说有可能是因为这两个函数也不是必须的)出现在Object.defineproperty的第三个参数描述符对象的属性中。
note: 我们由上面的属性可以看到存取器属性不具有可写性,它的可写性就是有getter和settter方法控制的。

1). 定义存取器属性最简单的方法是使用对象直接量语法的一种扩张写法。
var o = {
    data_prop: value,
    get accessor_prop() {
        return this.data_prop;
	},
	set accessor_prop(value) {
		this.data_prop = value;
	}
}
note: 这个函数定义没有使用function关键字,而是使用get和(或)set,这个地方也没有使用冒号讲属性名和函数体分隔开。

2). 使用Object.defineProperty()来定义
var book={
	_year:2004,
	edition:1
};


Object.defineProperty(book,"year",{
	get:function(){           
		return this._year;
	},
	set:function(newValue){
    	if(newValue>2004){
    		this._year=newValue;
    		this.edition+=newValue-2004;
   		}
 	}
});
note:_year属性使用下划线开头,这个一种常用的记号,用于表示只能通过对象方法访问的属性。

d. getOwnPropertyDescriptor用于获得属性的描述符,但是只能得到自有属性的描述符,不能得到继承属性的描述符,要想获得继承属性的特性,需要遍历原型链(具体的方法后面会有介绍)。
如下图:
         JavaScript基础内容_第11张图片

(7). 对象的三个属性


a. 原型属性
对象的原型属性使用来继承属性的,js中的对象是具有原型链的,关于原型链后面会有单独的章节介绍。这里只介绍一些基本的信息。

1). 原型:
通过对象直接量创建的对象的原型: Object.prototype属性
通过new创建的对象的原型: 构造函数的prototype属性
通过Object.create()的创建的对象使用第一个参数作为原型

2). Object.getPrototypeof()可以查询对象的原型
object.contructor.prototype也可以用来检测对象的原型。
note: 这两个方法检测原型的方式并不可靠,后面讲到原型链的时候会详细分析。

isPrototypeOf(): p.isPrototypeOf(o)用来检测p是否是o的原型
note: 对象还有一个__proto__对象,现在基本的浏览器都已经兼容,但是还是不推荐使用。

b. 类属性
对象的类属性是一个字符串,用于表示对象的类型信息。现在还没有提供设置这个属性的方法,只有一种间接的方法去查询它。
Object.prototype.toString.call(o).slice(8, -1);
其实从上面的方式中可以看到其实所有的对象都是拥有toString方法的,只是很对对象的toSring方法都已经重写了,所以只能上面的方式去获取。

c. 可扩展性
对象的可扩展性泳衣表示是否可以给对象添加新属性。所有内置对象和自定义的对象都是像是可扩展的,除非将对象转换为不可扩展的。

(8)序列化对象


a. JSON.stringify() 序列化对象,序列化就是将对象的状态转换为字符串
b. JSON.parse() 还原序列化对象。
c. JSON 的全称为 javascript Object notation
d. 序列化只能序列化对象可枚举的自有属性,上面说枚举属性的时候有例子。

6. 数组


(1). 数组是指的有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。


javascript数组是无类型的,数组元素可以是任意类型。并且在同一个数组中的不同元素也可以有不同的类型。


(2). 创建数组


a. 数组直接量: 
var array = [1, 2, , 4,];
array // [1, 2, undefined, 4]


note: 1). 数组中省略的元素被赋予undefined
          2). 数组直接量的语法允许有可选的结尾的逗号,所以上面的值是四个数。
          3). 数组中存在undefined的数组称为稀疏数组,所以稀疏数组的length属性大于数组内元素的个数。

b. 使用Array()构造函数。
1). var array = new Array(); 没有参数
2). var array = new Array(10);    只有一个数值的参数的时候是指定数组的长度。
3). var array = new Array(1, 2);
var array = new Array("string");
当参数为两个或者两个以上的时候是在定义数组的时候定义数组内的元素
当参数为一个非数值参数的时候也是在定义数组的时候定义数组内的元素

(3). 数组元素的读和写


使用[]操作符来访问数组中的一个元素。[]内是一个放回非负整数值的任意表达式。
note: 负数和非负整数的索引。会被当作属性值看待,而不是数组索引。如果用到是非负整数的字符串,就会被当作数组索引。如下图示例:
JavaScript基础内容_第12张图片

(4). 稀疏数组


稀疏数组就是包含从0开始的不连续索引的数组。
稀疏数组的length属性值大于元素的个数。

(5). 数组长度


每个数组有一个length属性。
1). 当数组元素的索引i大于或等于现在数组的长度时,length的属性值会自动是设置为i+1.
2). 设置length属性为一个小雨当前长度的非负整数n,当前数组中索引值大于或等于n的元素将从中删除。
3). 设置length属性大于当前数组的长度。
请看下面图示: 


(6). 数组元素的添加和删除


a. 新索引赋值:
a = [];
a[0] = 'zero';

b. pop()和push()方法
a.push('onw');
a.pop();

c. shift()和unshif()方法
shift: 在数组首部插入一个元素
unshift: 在数组首部删除一个元素

e. delete运算符
delete运算符最好还是不要使用,使用delete会形成稀疏数组,而且length属性不会变化。

(7). 数组遍历


a. for循环:遍历数组的最常见的方法。

b. for in 循环
由于for/in循环能都枚举继承的属性名,也就是会继承Array.prototype中的方法也会继承。所以基于这个原因,数组不能直接使用这个方法,除非使用额外的检测方法来过滤到不想要的属性。如果是在通过代码在Array.prototype添加的可枚举属性,也会在数组中遍历出来,当然内置的属性不会遍历到的。
所以使用这个方法的话,可以hasOwnProperty()进行过滤



c. ECMAScript 5定义了一些遍历数组元素的方法,按照索引的顺序按个传递刚给定义的函数,最常用的是forEach()方法

(8). 多维数组


Javascript不支持真正的多维数组,但可以用数组的数组来近似。

(9). 数组的方法


数组的常用方法:红颜色的方法是会产生新的数组

a. join(param):将数组中所有元素都转化为字符串并连接起来。Param表示以什么符号进行连接,默认使用逗号。

var array = [1, 2, 3];
console.log("方法操作的结果:" + array.join('-')); 
console.log("原数组值:" + array);

方法操作的结果:1-2-3
原数组值:1,2,3

b. reverse(): 将数组的元素颠倒顺序,然后返回。 PS:返回的还是原来的数组,并不会产生新的数组。

var array = [1, 2, 3];
console.log("方法操作的结果:" + array.reverse());
console.log("原数组值:" + array);

方法操作的结果:3,2,1
原数组值:3,2,1

c. sort(): 将数组元素排序并返回排序后的数组。 PS:返回的还是原来的数组,并不会产生新的数组。

默认元素以字母顺序进行排序(可以传进去一个比较函数进行排序),传递函数之后会按照函数中的顺序进行排序,请看下面的例子
var array = ['c', 'b', 'a'];
console.log("方法操作的结果:" + array.sort());
console.log("原数组值:" + array);

var array = [1, 2, 3];
console.log("方法操作的结果:" + array.sort(function(a, b){
    return a < b;
}));
console.log("原数组值:" + array);

方法操作的结果:a,b,c
原数组值:a,b,c
方法操作的结果:3,2,1
原数组值:3,2,1

d. concat(): 将数组创建并返回一个新的数组,如果参数中含有数组,则传递的是数组的元素,而不是数组本身。也不会改变调用的数组。PS:会生成新的数组

var array = ['c', 'b', 'a'];
var newArray = array.concat(4, [5, [6, 7]])
console.log("方法操作的结果:" + newArray);
console.log("原数组值:" + array);

方法操作的结果:c,b,a,4,5,6,7
原数组值:c,b,a

e. slice(start, end): 

start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。
end  可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。
PS:返回的是一个新的数组。
var array = [1, 2, 3, 4, 5];
var newArray = array.slice(0, 3)
console.log("方法操作的结果:" + newArray);

var newArray = array.slice(3)
console.log("方法操作的结果:" + newArray);
var newArray = array.slice(1, -1)
console.log("方法操作的结果:" + newArray);

var newArray = array.slice(-3, -2)
console.log("方法操作的结果:" + newArray);
console.log("原数组值:" + array);
                 
方法操作的结果:1,2,3
方法操作的结果:4,5
方法操作的结果:2,3,4
方法操作的结果:3
原数组值:1,2,3,4,5

f. splice(): 在当前数组中插入或者删除元素的通用方法。PS:不产生新的数组

a. 一个参数的时候,表示开始截取数组的索引位置。返回的是截取的数组。
b. 两个参数的时候,第一个参数不变,第二个参数表示的是截取几个元素。返回的截取的元素。
c. 大于两个参数的时候,后面的参数插入的元素值,如果之前插入的位置没有值,则返回的是[],如果原先有值,则返回的是原先的值。

var array = [1, 2, 3, 4, 5];
var newArray = array.splice(0, 3, [1, 2], 3)
console.log(newArray);
console.log(array);

[1, 2, 3]
[Array[2], 3, 4, 5]


g. pop, push, shift, unshift操作的是原来的数组,不会产生新的数组。


h. toString(): 输出用逗号分隔的字符串列表,输出不会包含方括号或者其他形式的包裹数组数值的分隔符。


(10). ECMAScript 5中的数组方法


a. forEach(): 只有一个参数时,传入的是数组元素。三个参数时(v, i, a):第一个是数组元素,第二个是数组索引,第三个是数组本身。      
note: forEach不能否在数组遍历完前终止遍历,除非把forEach()方法放在一个try块中,并能抛出一个异常。
function foreach(a,f,t){
    try { 
	a.forEach(f,t); 
    }
    catch(e){
         if(e === foreach.break)return;
              else throw e;
         }
}

foreach.break = new Error("StopIteration");
var a = [1, 2, 3, 4, 5];
foreach(a, function(value){
    if (value == 3) {
         throw foreach.break;
    }
})


b. map:调用数组的每个元素传递给指定的函数,并返回一个新数组,不影响原先数组的元素。
c. filter: 返回的数组元素是调用的数组的一个子集,传递的函数是用来逻辑判定的。
             使用filter去过滤稀疏数组中缺少的元素:var dense = space.split(function(){ return true; });
             使用filter压缩空缺并删除undefined, null元素,var a = a.filter(function(x){ return x !== undefind && x != null })
e. every, some: 数组的逻辑判断,返回true,false。
f. reduce():使用指定的函数将数组元素进行组合,生成单个值。先计算前两个值,计算结果之后在于第三个元素进行运算。reduceRight():按照索引的顺序从高到低(从右到左).
g. indexOf(): 搜素元在数组中的位置,返回第一个元素的索引,没有找到则返回-1,lastIndexOf(): 从数末尾进行搜索。存在第二个参数,指定一个索引,并从当前这个索引的位置进行搜索。

你可能感兴趣的:(JavaScript,JavaScript)