ES 6+ 内置对象
JavaScript 设计中内置了许多对象:Number、String、Boolean、Null、Undefined、Symbol、Object、Array、Date、Math、Function、RegExp、Error对象
对象声明
对象是一种特殊的引用数据类型,拥有属性和方法,JavaScript 中所有的事务都是对象:字符串、数值、数组、函数等
- 使用对象实例化方式创建对象:var obj = new Object();
- 使用对象字面量方式创建对象:var obj = {};
function say() {
console.log('miao');
}
var obj = new Object();
obj.name = 'Tom';
obj.age = 5;
obj.say = say; // 需要通过 obj.say() 方法调用
obj.say = say(); // 需要通过 obj.say 方法调用
var obj = {
name: 'Tom',
age: 5,
say: function() { // 匿名函数
console.log('miao');
}
};
通过使用函数来构造对象,就可以通过创建对象实例
function person(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
var tom = new person('Tom', 'boy', 12);
console.log(tom.name, tom.sex, tom.age);
var lily = new person('Lily', 'girl', 10);
console.log(lily.name, lily.sex, lily.age);
函数声明
函数是定义一次但却可以调用或执行任意多次的一段 JS 代码,函数有时会有参数,通过调用函数进行参数的传递,有些函数也会将函数体执行的结果返回出来,这个返回值也是函数调用表达式的值。
语法:function fun-name([args]) { 函数执行体; }
有、无参数函数
function message() { // 经常使用采用这种模式声明
console.log('hello world');
}
message(); // 函数本身是不执行的,只有被调用时才执行
var message = function() { // 执行体系大,采用这种模式声明
console.log('hello world');
}
function myAdd(a, b) {
console.log('两个数的和为:' + (a + b));
}
myAdd(2, 3); // 5
返回数据
函数通过 return 返回函数体执行的结果,在函数调用时返回,并赋值给一个变量,return 发生了执行,后续代码无意义,不执行
function message() {
return 'hello world';
}
var msg = message(); // msg = 'hello world'
function myAdd(a, b) {
return a + b;
}
var sum = myAdd(2, 3); // sum = 5
形参实参
- 形参:就是函数在声明定义的时候参数变量
- 实参:就是函数在调用传值的时候传递数据
JavaScript 中函数的形参设置多少,实参个数可以与形参个数不同,是被允许的,arguments 为函数的形参对象,是一个伪数组
arguments 有一个指针函数 callee(),用于指向 arguments 所在的函数,参考下面:# 内部指针
function addNum() {
for(var sum = 0, i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
var sum = addNum(2,5, 5); // sum = 12
var sum = addNum(12, 4); // sum = 16
匿名闭包回调
- 匿名函数:就是没有名字的函数,单独的匿名函数是没法自身运行和调用的,常用于赋值,也可通过表达式运行
- 闭包函数:就是函数内返回的一个可访问当前函数作用域的内部函数体,驻留局部变量,避免使用全局变量
- 回调函数:通过一个函数调用另外一个函数,回调函数通常作为一个函数的参数传递,等到这个函数执行完成之前,调用回调函数
function addNum(a, b) {} // 普通函数
var addNum = function(a, b) {}; // 等号右侧就是一个匿名函数,通过 addNum()
(function(a, b) {})(a, b); // 表达式调用匿名函数,JQuery 的封装就是一个表达式的匿名函数
// 把匿名函数执行结果赋值给变量,与上面不同,上面是把匿名函数赋值给变量,变量就成了 Function 引用类型
var addNum = (function(a, b) { return a + b; })(a, b);
--------------------------------------------------------------------------------------------------------------------------------
for (var i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
}
解析:上面的代码不会按预期显示值0,1,2,3 和 4,而是会显示 5,5,5,5 和 5,原因是:在循环中执行的每个函数将整个循环完成之后被执行,会导致计时器函数引用存储的最有一个 i = 5 的值,所以会输出:5,5,5,5,5
for (var i = 0; i < 5; i++) {
(function(x) {
setTimeout(function() { console.log(x); }, x * 1000 );
})(i);
}
--------------------------------------------------------------------------------------------------------------------------------
function cat(name, callback) {
callback(name + 'see Jerry');
}
cat('Tom', function(message) { console.log(message); }); // "Tom see Jerry"
内存回收
在 JavaScript 定义的全局变量是不会被立即回收的,只有当关闭浏览器时,才会触发回收机制,垃圾不回收容易导致运行卡顿、内存溢出等现象,对于内存管理,是开发者必备的一块技术点,如何优化 JavaScript 的内存管理:
- 善用函数,普通函数的创建,有时候会导致变量的提升,从而内存不会立即释放,推荐使用匿名函数方式
- 使用普通函数,需要手动解除变量引用,重置为 null,从而减少内存占用,释放内存
- 绝对不要定义全局变量,通过匿名函数传值发方式传递局部变量,也可以通过回调函数方式进行变量传值
for(var i = 0; i < 5; i++) {}
console.log(i); // 5 不会被销毁
// 使用块模块封装,处理全局变量内存管理
(function() {
for(var i = 0; i < 5; i++) {}
})();
console.log(i); // Uncaught ReferenceError: i is not defined
--------------------------------------------------------------------------------------------------------------------------------
;(function(document, window){
var btns = document.querySelectorAll('.btn');
var output = document.querySelector('#output');
var arrs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for(var i = 0; i < btns.length; i++) {
btns[i].onclick = (function(num){
return function() {
output.innerText += 'Clicked ' + num;
};
})(arrs[i]);
}
})(document, window);
内部指针
- arguments.callee() 返回正被执行的 Function 对象,常用于匿名函数的递归或者保证函数的封装性
- this 默认指向 window(JavaScript 里面最大的对象,最外围的对象),可以指向当前对象或引用对象
- apply() 用于设置 this 的指向,默认 this 指向 window,参数是那个对象,调用方法中的 this 就指向这个对象
function factorial(num) { // 数的阶乘计算
if (num <= 1) { return 1; }
return num * arguments.callee(num - 1); // 会存在一个调用记录,每次调用就会形成一个调用栈
}
var res = factorial(3); // !3 = 3 * 2 * 1 = 6
PS:不推荐使用 arguents.callee(),访问 arguments 是个很昂贵的操作,它是个很大的对象,每次递归调用时都需要重新创建,影响现代浏览器的性能,还会影响闭包,解决方法就是给内部函数指定一个名字,在条件满足是,尾调用这个函数
function factorial(num) { // 数的阶乘计算
return (function tailFactorial(num, total) {
if(num === 1) return total;
return tailFactorial(n - 1, n * total); // 尾调用自己
})(num, 1);
}
var res = factorial(3); // !3 = 3 * 2 * 1 = 6
--------------------------------------------------------------------------------------------------------------------------------
function currying(fn) {
return function(list) {
return fn.call(this, list); // 使用柯里化
}
}
function recursion(list) {
return (function tailRecursion(item, arr) {
arr.push(item + 1);
if(list.length === 0) return arr;
return tailRecursion(list.splice(0,1)[0], arr)
})(list.splice(0,1)[0], [])
}
var res = currying(recursion)([1, 2, 3, 4, 5]); // res = [2, 3, 4, 5, 6]
--------------------------------------------------------------------------------------------------------------------------------
function recursion(list, arr = []) { // ES 6 语法
var item = list.splice(0,1)[0];
arr.push(item + 1);
if (list.length === 0) {
return arr;
}
return recursion(list, arr);
}
var res = recursion([1, 2, 3, 4, 5]);
var color = 'red';
var car = {
name: '大众',
color: this.color // this 指向 window:是 JavaScript 里面最大的对象,最外围的对象
};
--------------------------------------------------------------------------------------------------------------------------------
var car = {
name: '大众',
text: 'green',
color: function() {
return this.text; // this 指向 car
}
};
car.color();
--------------------------------------------------------------------------------------------------------------------------------
function person() {
this.name = 'zhangsan'; // this 指向引用对象 person()
}
var zs = new person();
zs.name; // 'zhangsan'
--------------------------------------------------------------------------------------------------------------------------------
function say() {
console.log(this.text); // this 指向引用对象 cat
}
var cat = {};
cat.text = 'miao';
cat.say = say;
cat.say(); // 'miao'
var person = {
fullName: function() {
return this.fName + ' ' + this.lName; // 这里的 this 就对应到 apply 传入的对象
}
};
var zs = {
fName: 'zhang',
lName: 'san'
};
person.fullName.apply(zs); // zhang san
--------------------------------------------------------------------------------------------------------------------------------
function addNum1(a, b) {
console.log(this);
return a + b;
}
function addNum2(a, b) {
return addNum1.apply(this, [a, b]); // this 指向 window,apply 用于调用自身方法
}
addNum2(2, 3);
--------------------------------------------------------------------------------------------------------------------------------
function addNum1(a, b) {
console.log(this);
return a + b;
}
function addNum2(a, b) {
return addNum1.call(this, a, b); // this 指向 window,apply 用于调用自身方法
}
addNum2(2, 3);
变量作用域
JavaScript 内所有的函数的参数都是局部变量,没有引用传递方式
function message(text) {
text = '改变';
}
var text = '原来';
message(text); // text = '原来'
--------------------------------------------------------------------------------------------------------------------------------
function message(obj) {
obj.name = 'Tom';
}
var obj = new Object();
message(obj); // obj.name = 'Tom'
--------------------------------------------------------------------------------------------------------------------------------
function message(obj) {
obj.name = 'Tom';
var obj = new Object();
obj.name = 'Jerry'; // 由于重新声明了一个引用类型,地址不同
}
var obj = new Object();
message(obj); // obj.name = 'Tom'
- 全局变量:在外环境中,在任何地方都可以读取使用到的变量,所有的全局变量都是在 window 下
- 局部变量:在内代码块,仅在内部才能访问使用的变量,函数参数也是局部作用
function message() {
return 'this is message';
}
var msg = 'hello world';
console.log(window.message()); // this is message
console.log(window.msg); // hello world
--------------------------------------------------------------------------------------------------------------------------------
function message() {
var msg = 'hello world';
return 'this is message';
}
console.log(window.message()); // this is message
console.log(window.msg); // undefined
--------------------------------------------------------------------------------------------------------------------------------
function message() {
msg = 'hello world';
return 'this is message';
}
console.log(window.message()); // this is message
console.log(window.msg); // hello world (不推荐,全局变量最好写最外面)
作用域链
作用域链核心思想:变量的作用域始终是从里往外走,函数定义会预解析,变量不会
var name = 'tom';
function f() {
name = 'jerry';
}
f(); // name = 'jerry';
--------------------------------------------------------------------------------------------------------------------------------
var name = 'tom';
f(); // name = 'jerry';
function f() {
name = 'jerry';
}
--------------------------------------------------------------------------------------------------------------------------------
function f1() {
var name = 'tom';
function f2() {
var name = 'jerry';
function f3() {
var name = 'terry';
console.log(name); // name = 'terry'
}
f3();
}
f2();
}
--------------------------------------------------------------------------------------------------------------------------------
message(); // 预解析:“miao”
function message() {
console.log('miao');
}
--------------------------------------------------------------------------------------------------------------------------------
console.log(a); // undefined
var a = 10;
--------------------------------------------------------------------------------------------------------------------------------
var name = 'tom';
function show() {
console.log(name);
var name = 'jerry';
}
show(); // undefined
--------------------------------------------------------------------------------------------------------------------------------
console.log(show); // ƒ show() { console.log('function'); }
function show() {
console.log('function');
}
var show = 'var_value';
console.log(show); // 'var_value'
--------------------------------------------------------------------------------------------------------------------------------
var name = 'tom';
show();
function show() {
console.log(name); // undefined
var name = 'jerry';
}
--------------------------------------------------------------------------------------------------------------------------------
var tom = 'tom';
show();
function show() {
var jerry = 'jerry';
console.log(tom); // undefined
console.log(jerry); // 'jerry'
var tom = 'cat';
}
--------------------------------------------------------------------------------------------------------------------------------
show();
console.log(cat); // 会报错
console.log(mouse); // 'like'
console.log(dog); // 'like'
function show() {
var cat = mouse = dog = 'like'; // mouse 和 dog 没有使用 var 是全局变量
console.log(cat); // 'like'
console.log(mouse); // 'like'
console.log(dog); // 'like'
}
异常处理
- try :测试代码块的错误
- catch:处理错误
- throw:创建自定义错误
- finally:测试代码块后执行,不管有无异常
function message() {
try {
adddlert("Welcome guest!");
} catch(err) {
console.log(err);
}
}
message(); // ReferenceError: adddlert is not defined
--------------------------------------------------------------------------------------------------------------------------------
function myFunction(val) {
try {
if(val == '') throw 'empty';
if(isNaN(val)) throw 'not a number';
if(val > 10) throw 'too high';
if(val < 5) throw 'too low';
} catch(err) {
console.log(err);
}
}
myFunction(NaN); // not a number
myFunction(''); // empty
--------------------------------------------------------------------------------------------------------------------------------
function myClear() {
try {
throw new Error('出错了...');
} finally {
console.log('清理完成');
}
}
myClear(); // 清理完成
错误属性 | 属性描述 |
---|---|
name | 设置或返回一个错误名 |
message | 设置或返回一个错误信息(字符串) |
function say() {
try {
add(2, 3);
} catch(err) {
console.log(err.name); // ReferenceError
console.log(err.message); // ReferenceError
}
}