目录
2.6 对象
2.7函数
2.7.1 概述
2.7.2 函数的属性和方法
2.7.3 函数作用域
2.7.4 参数
2.7.5 闭包
2.7.6 立即调用函数表达式
2.7.7 eval命令(不推荐使用,省略。。。)
2.8 数组
2.9 typeof操作符
2.10 思维导图(本地的时候的图片好好的,上传就这样了。。。。看我打印出来的,好看多了。。。。。)
2.6 对象
1 概述
1)概念
对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。
2)键名,也叫属性/方法(值为函数时)
键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名);
如果键名是数值,会被自动转为字符串;
如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错。
对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”;如果属性的值还是一个对象,就形成了链式引用;
属性可以动态创建,不必在对象声明时就指定。
"use strict"; var obj = { "var": "hello", 1e2: "23", 3.2: "23", 0X78: 34, 1: "abu", //如果键名是数值,会被自动转为字符串。 //1p: "hello", //报错。 "1p":"hello", //不报错 p: function (x) { //如果对象的属性是函数,通常称p为对象的方法 return x; } };
var obj2 = { bar: "wo" }; obj.foo = obj2; console.log(obj.foo.bar); //wo。如果属性的值还是一个对象,就形成了链式引用。 obj.my = 123; //属性可以动态创建,不必在对象声明时就指定。 console.log(obj.my); //123 |
3) 表达式还是语句?(最好看原文)
如果行首是一个大括号,它到底是表达式还是语句?为了避免这种歧义,JavaScript 引擎的做法是,如果遇到这种情况,无法确定是对象还是代码块,一律解释为代码块。
如果行首是一个大括号且要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象
"use strict"; { foo: 123};//第一种可能是,这是一个表达式,表示一个包含foo属性的对象;第二种可能是,这是一个语句,表示一个代码区块,里面有一个标签foo,指向表达式123。 console.log({ foo: 123}); //{ foo: 123 } 这里不是行首 ({ foo: 1235});//如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。 //({console.log(123)}); //报错
//差异在eval语句(作用是对字符串求值)中反映得最明显。 console.log(eval('{foo: 123}'));// 123 console.log(eval('({foo: 123})')) ;// {foo: 123} |
44)对象的引用
如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量的属性和方法,会影响到其他所有变量。
2 属性
1)属性的读取
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
注意:如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。方括号运算符内部还可以使用表达式。数字键可以不加引号,因为会自动转成字符串。
注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。
"use strict"; var a = "bar"; var b = "bin"; var obj = { a: 23, bar: 21, b: "nji", 1: "ki" }; console.log(obj.a); //23 引用对象obj的foo属性时,如果使用点运算符,foo就是字符串; console.log(obj[a]); //21 如果使用方括号运算符,但是不使用引号,那么foo就是一个变量,指向字符串bar。 console.log(obj["a"]); //23 console.log(obj[b]); //undefined b是变量,指向字符串bin,但是因为在obj对象中没有定义键名为字符串bin的属性,故而为undefined //console.log(obj.1); //数字键不能使用点运算符,这里报错 console.log(obj[1]); //ki,数字键只能使用放括号运算符,加不加双引号都行 console.log(obj["1"]); //ki |
2)属性的赋值
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
3)属性的查看
Object.keys()
"use strict"; var obj = { 1: 12, bj: "br" }; console.log(Object.keys(obj)); //[ '1', 'bj' ]。查看一个对象本身的所有属性,可以使用Object.keys方法。 |
4)属性的删除:delete
delete命令只能删除对象本身的属性,无法删除继承的属性。即使delete返回true,该属性依然可能读取到值。
var obj = { 1: 12, bj: "br" }; console.log(Object.keys(obj)); //[ '1', 'bj' ]。查看一个对象本身的所有属性,可以使用Object.keys方法。 console.log(delete obj[1]); //返回true。delete命令用于删除对象的属性,删除成功后返回true。 console.log(Object.keys(obj)); //[ 'bj' ] console.log(obj[1]); //undefined console.log(delete obj[1]); //返回true 。注意,删除一个不存在的属性,delete不报错,而且返回true。
//只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。 var obj = Object.defineProperty({}, 'p', { value: 123, configurable: false });
console.log(obj.p); // 123 console.log(delete obj.p );// false。严格模式下会报错 |
5)属性是否存在:in运算符
in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值)
in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。
"use strict"; var obj = { 1: 12, bj: "br" }; //in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。 console.log(1 in obj); //true console.log("toString" in obj); //true。in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。 //可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。 console.log(obj.hasOwnProperty("toString")); //false |
5)属性的遍历 for…in循环
for...in循环用来遍历一个对象的全部属性。
注意:它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。它不仅遍历对象自身的属性,还遍历继承的属性。对象继承而来的toString属性是不可遍历的。
如想遍历自身属性,for…in结合hasOwnProperty()方法
"use strict"; var obj = { 1: 12, bj: "br" }; for(var key in obj){ if(obj.hasOwnProperty(key)){ console.log(key,obj[key]); } } /*输出 1 12 bj br */ |
3 with语句(不建议使用)
它的作用是操作同一个对象的多个属性时,提供一些书写的方便。
注意,如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。
注意:严格模式已经弃用with语句
//"use strict"; var obj = { 1: 12, bj: "br", p1: 1 }; with (obj){ p1 = 2; p2 = 3; //果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。 } console.log(obj.p1); //2 console.log(obj.p2); //undefined console.log(p2); //3 //另外这是因为with区块没有改变作用域,它的内部依然是当前作用域。这造成了with语句的一个很大的弊病,就是绑定对象不明确。 |
2.7函数
2.7.1 概述
1 函数的声明
1)使用function命令
function 函数名(参数) { //函数体 } |
2) 函数表达式:变量赋值的方法
"use strict"; //将一个函数赋值给变量。这时,这个函数又称函数表达式(Function Expression) var myF =function x() { //一般情况下,函数名可以不用。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。 console.log(typeof x);//一般情况下, }; |
3)Function构造函数(基本上不用)
"use strict"; var add = new Function( "x", // "y", // "return x+y" //除了这个参数是函数体,其他都是add的参数 ); console.log(add(1,2)); //3 |
2 函数的重复声明
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。而且,由于函数名的提升(参见下文),前一次声明在任何时候都是无效的。
3
函数调用时使用:圆括号运算符
return语句:函数的返回,遇到return语句,则退出整个函数;如果没有return语句,则返回undefined
递归:函数调用自身
4 函数作为“值”特性
JS中,函数可以看成一种值,可以赋值给变量、对象,甚至函数的参数或返回值等。
5 函数名的提升
JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
1)采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部
2)如果采用赋值语句定义函数,JavaScript 就会报错。
3)如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义
"use strict"; // 1 采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部 fun(); //返回“函数被提升了”。 function fun() { console.log("函数被提升了"); // }
//2 如果采用赋值语句定义函数,JavaScript 就会报错。 fun1(); //报错,fun1 is not a function。原因是变量fun1只是被提升,并没有被赋值 var fun1 = function () { console.log("我没有被提升"); };
// 3 如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义 function myFun() { console.log("我是使用Function声明"); } var myFun = function () { console.log("我是函数表达式赋值给变量"); }; myFun();//我是函数表达式赋值给变量 |
2.7.2 函数的属性和方法
1 name属性
"use strict"; //1 函数的name属性返回函数的名字。 function fun() { console.log("11"); } console.log(fun.name); //fun
//2 如果是通过变量赋值定义的匿名函数,那么name属性返回变量名。 var myFun = function(){ //第2种情况 }; console.log(myFun.name); //myFun
//3 如果是通过变量赋值定义的具名函数,那么name属性返回function关键字之后的函数名。 var myFun1 =function myName() { //函数体 }; console.log(myFun1.name); //myName |
2 length属性
函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。
3 toString()函数
1)返回一个字符串,内容是函数的源码。
2)函数内部的注释也可以返回。利用这个特性可以实现多行字符串
"use strict"; //利用函数的toString()方法能返回函数的内部注释,返回多行字符串 function myFun() { /* 我是 多行 字符串 */ } function mulString(fun) { var arr = fun.toString().split("\n"); return arr.slice(2, arr.length - 2).join("\n"); } console.log(mulString(myFun)); //记住这里的myFun后面不能加括号,否则会报错 /*输出如下: 我是 多行 字符串 */ |
2.7.3 函数作用域
1 作用域
1)定义:作用域指的是变量存在的范围
2)分类
全局作用域(ES5):变量在整个程序一直存在,所有地方可以读取。全局作用域中的变量称谓全局变量
函数作用域(ES5):变量只在函数内部。局部作用域中的变量称为局部变量,函数内部定义的变量,会在该作用域内覆盖同名全局变量。
块级作用域(ES6中):暂时不学习
//"use strict"; var a = 2; //全局变量 function f() { var a = 1; //局部变量,在函数内部会覆盖掉全局变量a var b= 2; //局部变量 c = 3; //严格模式下报错。非严格模式下,在函数内部不使用var声明的变量是全局变量,函数外面也能访问 console.log(a); } console.log(c); //这里报错,因为在函数内部定义的全局变量,只有在调用函数一次之后,变量才会有定义 f(); //1 console.log(b); //报错,无法调用函数内部的局部变量 console.log(c); //3 |
2 函数内部变量的提升
var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
3 函数本身的作用域
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
"use strict"; //1 函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。 var a = 1; var x = function () { console.log(a); }; function f() { var a = 2; x(); //其作用域是全局作用域,而不是f()函数的作用域 } f(); // 1
//2 如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。 var myF = function () { console.log(b); }; function myY(f) { var b = 2; f(); // } myY(myF); //报错:b is not defined。因为传入参数myF之后,myF函数的作用域是全局函 //全局作用域,不会调用myY()函数内部的变量
//3 函数内部声明的函数,作用域绑定在函数内部,即闭包 |
2.7.4 参数
1 调用一个函数,有时需要提供参数
2 参数的省略
JavaScript 允许省略参数,省略的参数的值就变为undefined。
没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入undefined。
3 传递方式
1)基本数据类型的值(数值、字符串、布尔值):传值传递(在函数体内修改参数值,不会影响到函数外部。)
2)复杂数据类型:传址传递(传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。)
注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉(注意对象的属性项数需要一致)整个参数,这时不会影响到原始值。
4 同名参数
如果有同名的参数,则取最后出现的那个值。
//"use strict"; function f(a, a) { console.log(a); }
f(1); // undefined。调用函数f的时候,没有提供第二个参数,a的取值就变成了undefined。严格模式下报错 |
5 arguments对象
1)arguments对象包含了函数运行时的所有参数,这个对象只有在函数体内部,才可以使用。
2)修改
正常模式:arguments对象与函数参数具有联动关系。
严格模式:arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数。
3)length属性
判断函数调用时到底带几个参数。
4)与数组的关系:类似数组,实际为对象
转换成数组方法:slice方法以及逐一填入新数组(常用for循环数组的push方法)
5)callee属性(严格模式禁用、不建议使用)
返回它所对应的原函数。
2.7.5 闭包
1 JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。但是,函数外部无法读取函数内部声明的变量。
2 作用域链
函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
"use strict"; function f1() { var n = "JavaScript"; function f2() { console.log(n); } return f2; } |
3闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,将函数内部和函数外部连接起来的一座桥梁。
4用处1:一个是可以读取函数内部的变量;另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
"use strict"; function createIncrementor(start) { return function () { return start++; }; } var inc = createIncrementor(5); //记住这里需要将函数的调用赋值给一个变量,如果不赋值给变量,多次调用只是简单的调用而已 //start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。 console.log(inc()); // 5 console.log(inc()); // 6 console.log(inc()); // 7 |
用处2:是封装对象的私有属性和私有方法。。
"use strict"; function Person(name) { var _age; function setAge(n) { _age = n; } function getAge() { return _age; } return { name: name, getAge: getAge, setAge: setAge }; } var p1 = Person('张三'); p1.setAge(25); console.log(p1.getAge()); // 25 //函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。 |
5 注意,闭包消耗内存大,滥用闭包,会造成网页性能问题。
2.7.6 立即调用函数表达式
1 定义以及格式
函数定义时立即使用
(function(){ /* code */ }()); // 或者 (function(){ /* code */ })(); |
2 扩展体
var i = function(){ return 10; }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }(); !function () { /* code */ }(); |
3.目的以及通用格式
一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
// 写法一 var tmp = newData; processData(tmp); storeData(tmp);
// 写法二(推荐使用) (function () { var tmp = newData; processData(tmp); storeData(tmp); }()); |
2.7.7 eval命令(不推荐使用,省略。。。)
1)作用:接受一个字符串作为参数,并将这个字符串当作语句执行。
2.8 数组
1 定义
1)数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
2)除了在定义时赋值,数组也可以先定义后赋值。
3)任何类型的数据,都可以放入数组。
4)多维数组
2 本质:对象
1)typeof运算符会返回数组的类型是object。
2)特点:键名是按次序排列的一组整数(0,1,2...)
3)数组的键名其实也是字符串。之所以可以用数值读取(用方括号结构读取),是因为非字符串的键名会被转为字符串。(读取和赋值时均成立)
3 length属性:返回数组的成员数量
1)length具有动态性、可写性(可以人为设置大小)
2)等于键名中的最大整数加上1。
3)由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响length属性的值。
"use strict"; //以下数组的键分别设为字符串和小数,结果都不影响length属性。 // 因为,length属性的值就是等于最大的数字键加1,而这个数组没有整数键,所以length属性保持为0。 var arr = ["JavaScript"]; console.log(arr.length); //1 arr['p'] = 'abc'; console.log(arr.length); //1 arr[2.1] = 'abc'; console.log(arr.length); //1
//如果数组的键名是添加超出范围的数值,该键名会自动转为字符串。 arr[-1] = 'a'; arr[Math.pow(2, 32)] = 'b'; console.log(arr.length); //1 console.log(arr[-1]); //a console.log(arr[4294967296]); //b。取键值时,数字键名会默认转为字符串。 |
4 in运算符
1)作用
检查某个键名是否存在
2)注意:
如果数组的某个位置是空位,in运算符返回false。
"use strict"; var arr = ["a", "b", "c"]; console.log(arr.length); //3。数组的长度为3 console.log(Object.keys(arr)); //[ '0', '1', '2' ]。返回的是数组中存在的键名
console.log(2 in arr); //true console.log("2" in arr); //true console.log(9 in arr); //false。数组中的键名没有“9”,返回false
arr[8] = "h"; //这样设置之后,数组中多了一个名为“8”的键名,长度为9 console.log(arr.length); //9。数组的长度为9,数组的length属性为键名中最大正数加1 console.log(Object.keys(arr)); //[ '0', '1', '2', '8' ] console.log("8" in arr); //true console.log(6 in arr); //false。数组中的键名没有“6”,返回false |
5 数组的遍历
1)for...in不仅会遍历数组所有的数字键,还会遍历非数字键。(不推荐)
2)for循环和while循环,果使用的是数组的length属性,则只会访问数字键。
"use strict"; var arr = [1, 2, 3, 4]; arr.foo = "非数字键"; //数组可以动态添加属性,键名为字符串 arr["jio"] = "Java";
//使用for ...in循环,会遍历数字键和非数字键 for (key in arr){ console.log(key, arr[key]); } /* 输出为: 0 1 1 2 2 3 3 4 foo 非数字键 jio Java */
/* 使用for或者while循环遍历数组,如果使用的是数组的length属性,则只会访问数字键。 */ for(var i = 0; i < arr.length; i++){ console.log(arr[i]); }
var j = 0; while (j < arr.length){ console.log(arr[j]); j++; } /* for和while循环中输出均为 1 2 3 4 */ |
3)forEach()访问数组(Array标准库中细说)
6 数组的空位
1)当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。
2)特性
A 空位是可读取的,读取时返回undefined;
B 使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性;
"use strict"; var arr = [1, , 2, 3]; delete arr[3]; console.log(arr.length); //4。使用delete命令不会影响length属性 console.log(arr); //[ 1, <1 empty item>, 2, <1 empty item> ]。 |
C 数组的某个位置是空位(没有元素),与某个位置是undefined(有元素,只是值为undefined而已),是不一样的。如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过。如果某个位置是undefined,遍历的时候就不会被跳过。
7 类似数组的对象
1)定义
如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object)。
2)特征
“类似数组的对象”的根本特征,就是具有length属性。但是length属性不是动态值,不会随着成员的变化而变化。
并且,不具有数组具有的方法
3)典型代表
arguments对象,以及大多数 DOM 元素集,还有字符串。
4)使用数组的slice方法转换成真正的数组(。。。)
var arr = Array.prototype.slice.call(arrayLike); |
55)以通过call()把数组的方法放到对象上面,然后使用数组的方法(。。。)
function print(value, index) { console.log(index + ' : ' + value); }
Array.prototype.forEach.call(arrayLike, print); // arrayLike类似一个数组 |
2.9 typeof操作符
1 typeof操作符意义:检测给定变量的数据类型。
2 JS中有三种方法确一个值是什么类型
1)typeof运算符
2)instanceof运算符(后面介绍)
3)Object.prototype.toString()方法(后面介绍)
3 typeof操作符返回值:undefined、boolean、number、string、function(函数时返回)、object(这个值是对象、null和数组时)
"use strict"; //表示严格模式 var a = undefined; var b = true; var c = 10; var d = "str"; function myFunction(){ return 1; } var e = null; var g = [1, 2, 3]; console.log(typeof(a)); //undefined console.log(typeof(b)); //boolean console.log(typeof(c)); //number console.log(typeof(d)); //string console.log(typeof(myFunction)); //function console.log(typeof(myFunction())); //number 请注意这里,注意,为何自己思考。 console.log(typeof(e)); // object console.log(typeof(g)); // object |
4 注意:在Safari5以及之前版本、Chrome7以及之前版本,正则表达式调用typeof会返回“function”,其他为“object”
2.10 思维导图(本地的时候的图片好好的,上传就这样了。。。。看我打印出来的,好看多了。。。。。)



如有错,请指出,如有侵权,请联系我,谢谢!
参考资料
1 JavaScript 教程https://wangdoc.com/javascript/basic/grammar.html
2 《JavaScript高级程序设计 第3版》