1. 数组
1.1
init
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
两种方式创建数组:
1. new Array(); (对象)
2. [1,2,3,4...]; (字面量)
-->
<script>
var array = [1, 2, 'fs', true]; //可以存放任意类型的数据
console.log(array);
console.log(array[2]);
array.length = 8; //手动修改数组长度
console.log(array);
var Array = [1,1,3];
Array[3] = 4; //增加了一个元素
console.log(Array);
Array = "Hello World!"; //将原来Array中的元素给全部覆盖了
console.log(Array);
</script>
</head>
<body>
</body>
</html>
1.2
数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// var arr = [1,2,3];
var arr = new Array();
var arr1 = new Array(2); //长度为2,为empty*2
var arr2 = new Array(2, 3); //分别是2,3
console.log(arr1);
console.log(arr2);
//检测是否为数组
console.log(arr1 instanceof Array); //way1
console.log(Array.isArray(arr2)); //way2
//
arr.push('hello'); //尾插
arr.unshift(1); //头插
console.log(arr);
arr.pop(); //尾删
arr.shift(); //头删
console.log(arr);
//
var Arr1 = ['pink', 'red', 'green'];
Arr1.reverse();
console.log(Arr1);
var Arr2 = [13, 4, 77, 1, 7];
// Arr2.sort(); //并不是想象中的排序
// console.log(Arr2);
// 如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,
// 说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,
// 首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
var arr = new Array(6)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
arr[3] = "James"
arr[4] = "Adrew"
arr[5] = "Martin"
document.write(arr + "<br />")
document.write(arr.sort())
//
// 请注意,上面的代码没有按照数值的大小对数字进行排序,要实现这一点,就必须使用一个排序函数
Arr2.sort(function (a, b) {
return a - b; //升序
// return b - a; //降序
});
console.log(Arr2);
//indexof(),lastindexof()
//toString()
var ARR = [1, 2, 3];
console.log(ARR); //[1,2,3]
console.log(ARR.toString()); //1,2,3 ---字符串
console.log(ARR.join('#')); //1#2#3 ---字符串
</script>
</head>
<body>
</body>
</html>
2. 函数
2.1
函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
函数的定义:
1. 命名函数;
2. 匿名函数;
3. new Function('arg1', 'arg2', 'arg3'); ( 里面的参数必须都是字符串格式 ) ( 不太常用 )
4. 所有函数都是Function的实例对象;
5. 函数也属于对象; (add instanceof Object)
-->
<script>
//1. 命名函数**********************************************************
function sum() {
var sum = 0;
for (var i = 0; i <= 100; i++) {
sum += i;
}
console.log(sum);
}
sum();
//2. 函数表达式(匿名函数)*************************************************
var NUM = function (num) {
console.log('functioal expression!');
console.log(num);
}
NUM(111);
//3. new Function() ****************************************************
var f = new Function('a', 'b', 'console.log(a + b)');
f(999, 1000);
console.log(f instanceof Object); // true
//3. 带参函数************************************************************
function add(a, b) {
console.log(a + b);
}
add(1, 2); //3
add(1, 2, 3); //3(多余的参数直接被忽略)
add(1); //NaN
//4. 函数返回************************************************************
//(1)eg1
function sub(num1, num2) {
return num1 - num2;
}
console.log(sub(3, 5));
//(2)eg2
function getArrayMax(arr) {
var max = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
console.log(getArrayMax([2, 4, 1, 5, 2, 6, 4]));
//(3)eg3
function result(num1, num2) {
return [num1 + num2, num1 - num2, num1 * num2];
}
console.log(result(1, 4));
//5. 嵌套调用************************************************************
function out() {
console.log('i am out!');
In();
}
out();
function In() {
console.log('i am in!');
}
//6. other
function test() {}
console.log(test()); // test()没有return, 则返回undefined
</script>
</head>
<body>
</body>
</html>
2.2
arguments伪数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
/*
1. 当我们不确定有多少个参数传递的时候,可以用arguments对象来获取;
在JS中,arguments实际上它是当前函数的一个内置对象;
[所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参!]
arguments对象的展示形式是一个[伪数组],因此可以进行遍历;
2. 作用:有了arguments就不用在函数中写形参了;
3. 伪数组具有以下特性:
1. 具有length属性;
2. 按索引方式存储数据;
3. 不具有数组的push,pop等方法;
4. (1) 正常模式下,arguments对象可以在运行时修改;
(2) 严格模式下,arguments对象是一个只读对象,修改它是无效的,但不会报错;
*/
function fun() {
console.log(arguments);
console.log(arguments.length);
console.log(arguments[1]);
for(var i =0 ;i < arguments.length;i++) {
console.log(arguments[i]);
}
}
fun(1,2,3,4,5);
/**************************************************************************/
function max() {
var max = arguments[0];
for(var i = 0;i < arguments.length;i++) {
if(arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(max(1,2,3));
console.log(max(1,2,3,4,5));
console.log(max(11,21,63,98,21,12));
</script>
</head>
<body>
</body>
</html>
2.3
函数的调用方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--
函数的调用方式:
1. 普通函数的调用;
2. 对象的方法的调用;
3. 构造函数;
4. 事件绑定函数;
5. 定时器函数;
6. 立即执行函数;
-->
</head>
<body>
<script>
// 1. 普通函数
function fn() {
console.log('人生的巅峰');
}
fn();
fn.call();
// 2. 对象的方法
var o = {
sayHi: function() {
console.log('人生的巅峰');
}
}
o.sayHi();
// 3. 构造函数
function Star() {};
new Star();
// 4. 绑定事件函数
// btn.onclick = function() {}; // 点击了按钮就可以调用这个函数
// 5. 定时器函数
// setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次
// 6. 立即执行函数: 自动调用
(function() {
console.log('人生的巅峰');
})();
</script>
</body>
</html>
2.4
函数中的this指向问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--
函数中的this指向问题:
1. 普通函数的调用; this指向window;
2. 对象的方法的调用; this指向对象;
3. 构造函数; this指向实例对象;
4. 事件绑定函数; this指向调用者, 比如按钮;
5. 定时器函数; this指向window;
6. 立即执行函数; this指向window;
-->
</head>
<body>
<script>
// 1. 普通函数
function fn() {
console.log('人生的巅峰');
}
fn();
fn.call()
// 2. 对象的方法
var o = {
sayHi: function() {
console.log('人生的巅峰');
}
}
o.sayHi();
// 3. 构造函数
function Star() {};
new Star();
// 4. 绑定事件函数
// btn.onclick = function() {};
// 5. 定时器函数
// setInterval(function() {}, 1000);
// 6. 立即执行函数: 自动调用
(function() {
console.log('人生的巅峰');
})();
</script>
</body>
</html>
2.5
高阶函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
高阶函数:是对其他函数进行操作的函数, 它接收函数作为参数 或 将函数作为返回值输出;
-->
</head>
<body>
<script>
// 1. 函数作为参数进行传递
function fun(a, b, callback) {
console.log(a + b);
callback && callback();
}
fun(1, 2, function () {
console.log('last call!');
});
</script>
<script>
// 2. 返回函数
function Fun() {
return function(a, b) {
console.log(a - b);
}
}
var _f = Fun();
_f(1, 3);
</script>
</body>
</html>
2.6
值址传递
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
1. 值传递: 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是```值传递```;
```这意味着,在函数体内修改参数值,不会影响到函数外部```;
var num = 2;
function f(_num) {
_num = 3;
}
f(num);
console.log(num); // 2
*/
/*
2. 址传递:但是,如果函数参数是复合类型的值(```数组、对象、其他函数````),传递方式是```传址传递```;
也就是说,传入函数的是原始值的地址,因此在函数内部修改参数,将会影响到原始值;
var obj = {
num: 1
};
function fff(o) {
o.num = 222;
}
fff(obj);
console.log(obj); // {num: 222}
*/
/*
3. 注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值;
下面代码中,在函数test内部,参数对象obb被整个替换成另一个值, 这时不会影响到原始值;
这是因为,形式参数(o)的值实际是参数obb的地址;
重新对o赋值导致o指向另一个地址,保存在原地址上的值当然不受影响;
*/
var obb = [1, 2, 3];
function test(o) {
o = [4, 5, 6];
}
test(obb);
console.log(obb); // [1,2,3]
</script>
</body>
</html>
2.7
改变this指向的三个方法
(1)
call()方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。
有时,需要把this固定下来,避免出现意想不到的情况。
JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
-->
<!--
call(thisValue, arg1,arg2...):
1. 调用函数; (add.call();)
2. 修改函数运行时的this指向;
-->
</head>
<body>
<script>
// [1.] 函数实例的call方法,可以指定函数内部this的指向,然后在所指定的作用域中,调用该函数。
var obj1 = {};
var fun = function () {
return this;
};
console.log(fun() === window); //true
console.log(fun.call(obj1) === obj1); // true, call方法可以改变this的指向,指定this指向对象obj;
// [2.] call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象(window)。
var num = 123;
var OBJ = {
num: 456
};
function callback() {
console.log(this);
console.log(this.num);
}
callback.call(); // Window 123
callback.call(null); // Window 123
callback.call(undefined); // Window 123
callback.call(window); // Window 123
callback.call(OBJ); // OBJ 456
// [3.] call的主要作用是实现继承
function Father(uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son(uname, age, sex) {
Father.call(this, uname, age, sex);
}
var son = new Son('刘德华', 18, '男');
console.log(son);
</script>
<!-- call方法的一个应用是调用对象的原生方法。(后话) -->
<script>
var object = {};
console.log(object.hasOwnProperty('toString')); //false
object.hasOwnProperty = function () {
return true;
};
console.log(object.hasOwnProperty('toString')); //true
//使用call调用对象的原生方法
console.log(Object.prototype.hasOwnProperty.call(object, 'toString')); //false
//上面代码中,hasOwnProperty是obj对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。
//call方法可以解决这个问题,它将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论obj上有没有同名方法,都不会影响结果。
</script>
</body>
</html>
(2)
apply()方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。
唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
func.apply(thisValue, [arg1, arg2, ...])
第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。
原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。
-->
<script>
// (0) 初步使用
function fun(num1, num2) {
console.log(num1 + num2);
}
fun.call(null, 1, 3);
fun.apply(null, [1, 4]); //apply() 接受一个数组作为参数, 1、4依次作为fun()函数的参数
// (1) 找出数组最大元素
// JavaScript 不提供找出数组最大元素的函数。结合使用apply方法和Math.max()方法,就可以返回数组的最大元素。
// Math.max(value1, value2, ...) ;
var arr = [10, 2, 3, 45, 1];
console.log(Math.max.apply(null, arr)); // arr的所有成员依次作为参数,传入max函数, 相当于max(10, 2, 3, 45, 1)
console.log(Math.max.apply(Math, arr)); // 这样写更为合适
//(2) 将数组的空元素变为undefined (后话)
//通过apply方法,利用Array构造函数将数组的空元素变成undefined。
var array = ['a', , 'b'];
Array.apply(null, array);
console.log(array); // ["a", empty, "b"]
// 空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果。
var testArray = ['a', , 'b'];
function print(value) {
console.log(value);
}
testArray.forEach(print); // a b => 跳过了空元素
Array.apply(null, testArray).forEach(print); // a undefined b
</script>
<script>
//(3) 转换类似数组的对象 (后话)
// 另外,利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。
console.log(Array.prototype.slice.apply({
0: 1,
length: 1
})); //[1]
console.log(Array.prototype.slice.apply({
0: 1
})); //[]
console.log(Array.prototype.slice.apply({
0: 1,
length: 2
})); //[1,empty]
console.log(Array.prototype.slice.apply({
length: 1
})); //[empty]
// 上面代码的apply方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的
// 从上面代码可以看到,这个方法起作用的前提是,被处理的对象必须有length属性,以及相对应的数字键。
var _A = new Array();
console.log(_A);
</script>
</body>
</html>
(3)
bind方法1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
bind()方法用于改变函数体内this的指向, 然后返回一个新函数;
bind()方法不会调用函数, 但是能改变函数内部的this指向;
fun.bind(thisArg, arg1, arg2, ...);
返回值:返回由指定的this值和初始化参数改造的原函数拷贝;
-->
<!--
何时用?
如果有的函数我们不需要立即调用, 但是又想改变这个函数内部的this指向, 此时用bind最合适了;
-->
</head>
<body>
<button>点击</button>
<script>
var object = {
name: 'tom'
}
function fun(a, b) {
console.log(this); // this此时指向object
console.log(a + b);
}
var f = fun.bind(object, 1, 3);
f();
</script>
<script>
// 我们有一个按钮, 当我们点击了之后, 就禁用这个按钮, 1秒后重新启用;
// 法一:注意定时器中的this是window, 所以之前的做法是 var that = this, that.disabled = false;
// 法二:bind();
var btn = document.querySelector('button');
btn.onclick = function () {
this.disabled = true;
setTimeout(function() {
this.disabled = false;
}.bind(this), 1000); // 此时定时器内部的this就是btn了
// 此处bind()函数中的参数可以是btn, 也可以是this, 都是代指btn; 但是this是最好的写法, 因为btn可能随时改变
}
</script>
</body>
</html>
(4)
bind方法2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
bind()方法用于将函数体内的this绑定到某个对象, 然后返回一个新函数;
bind()方法不会调用函数, 但是能改变函数内部的this指向;
fun.bind(thisArg, arg1, arg2, ...);
返回值:返回由指定的this值和初始化参数改造的原函数拷贝;
-->
<script>
var d = new Date();
console.log(d.getTime());
var show = d.getTime;
// show(); // Uncaught TypeError: this is not a Date object.
// 上面代码中,我们将d.getTime方法赋给变量show,然后调用show就报错了。
// 这是因为getTime方法内部的this,绑定Date对象的实例,赋给变量print以后,内部的this已经不指向Date对象的实例了。
// bind方法可以解决这个问题:
var print = d.getTime.bind(d);
print(); //1613100200254
</script>
<script>
// bind方法的参数就是所要绑定this的对象
var counter = {
count: 0,
inc: function() {
this.count++;
}
};
var func = counter.inc.bind(counter); //意思是将counter对象的inc方方法绑定到counter对象上,然后赋值给func
func();
console.log(counter.count); //1
// 上面代码中,counter.inc方法被赋值给变量func。
// 这时必须用bind方法将inc内部的this,绑定到counter,否则就会出错。
</script>
<script>
// bind还可以接受更多的参数,将这些参数绑定原函数的参数。
var add = function(x,y) {
return x * this.m + y*this.n;
};
var obj = {
m: 2,
n: 2
};
var newAdd = add.bind(obj,5);
console.log(newAdd(5)); //20
// 上面代码中,bind方法除了绑定this对象,还将add函数的第一个参数x绑定成5,
// 然后返回一个新函数newAdd,这个函数只要再接受一个参数y就能运行了。
// 如果bind方法的第一个参数是null或undefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window)。
function sum(x,y) {
return x + y;
}
var plus = sum.bind(null,5);
console.log(plus(6)); //11
// 上面代码中,函数add内部并没有this,使用bind方法的主要目的是绑定参数x,
// 以后每次运行新函数sum,就只需要提供另一个参数y就够了。
console.log('----------');
</script>
<script>
// bind方法有一些使用注意点:
//(1) 每一次返回一个新函数;
//(2)结合回调函数使用;
// 回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。
// 解决方法就是使用bind方法
var Counter = {
count: 0,
Inc: function() {
'use strict';
this.count++;
}
};
function callIt(callback) {
callback();
}
callIt(Counter.Inc.bind(Counter));
console.log(Counter.count); //1
// 上面代码中,callIt方法会调用回调函数。
// 这时如果直接把counter.inc传入,调用时counter.inc内部的this就会指向全局对象。
// 使用bind方法将counter.inc绑定counter以后,就不会有这个问题,this总是指向counter。
</script>
<script>
var person = {
age : 12,
add: function() {
this.age++;
}
};
var Fun = person.add.bind(person);
Fun();
console.log(person.age); //13
</script>
</body>
</html>
(5)
call-apply-bind区别
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--
相同点:都可以改变函数内部的this指向;
不通点:
1. call 和 apply 都会调用函数;
2. call 和 apply的区别在于call传递的是arg1, arg2...参数, 而apply传递的参数必须是数组形式;
3. bind 不会调用函数;
应用场景:
1. call 经常做继承;
2. apply经常跟数组有关系; (比如借助数学对象实现数组最大值最小值)
3. bind 不调用函数, 还想改变this指向; (比如改变定时器内部的this指向)
-->
</body>
</html>
2.8
函数的递归
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var num = 1;
function fn() {
if (num == 6) {
return;
} else {
console.log('hello');
}
num++;
fn();
}
fn();
</script>
<script>
// 求阶乘
function fun(num) {
if (num === 1) {
return 1;
}
return num * fun(num - 1);
}
console.log(fun(3));
</script>
<script>
// 利用递归遍历数据: 输入id号, 返回对应的数据对象;
// 存在重大bug( 后话 )
var array = [{
id: 1,
name: '家电系列',
goods: [{
id: 11,
gname: '冰箱系列',
goods: [{
id: 111,
gname: '海尔冰箱'
}, {
id: 112,
gname: '美的冰箱'
}]
}, {
id: 12,
gname: '洗衣机系列',
goods: [{
id: 121,
gname: '1类洗衣机'
}, {
id: 122,
gname: '2类洗衣机'
}]
}]
}, {
id: 2,
name: '服饰系列'
}];
function getId(_array, id) {
var obj = {};
_array.forEach(function(item) {
if(item.id === id) {
obj = item;
} else if(item.goods && item.goods.length > 0) {
obj = getId(item.goods, id);
}
})
return obj;
}
console.log(getId(array, 1));
console.log(getId(array, 2));
console.log(getId(array, 11));
console.log(getId(array, 111));
console.log(getId(array, 112));
console.log(getId(array, 12));
console.log(getId(array, 121));
console.log(getId(array, 122));
</script>
<script>
function test() {
return { name: 'XingWei', age: 23} ;
}
console.log(test());
</script>
</body>
</html>
2.9
闭包
(1)
闭包1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
1. 闭包: 指 有权访问另一个函数作用域中变量的 函数;
首先,闭包也是一个函数;
2. 闭包的作用:延伸了变量的作用范围;
闭包的另一个用处,是封装对象的私有属性和私有方法;
-->
</head>
<body>
<ul class="test">
<li>hello</li>
<li>world</li>
<li>love</li>
<li>you</li>
</ul>
<script>
// 1. fn就是一个闭包函数;
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
// 2. 闭包的作用:延伸了变量的作用范围, 此时f()函数也可以使用num变量了! (num是函数中的局部变量, 默认不能在外面访问, 通过闭包延伸了变量的作用域范围)
function Fun() {
var num = 20;
return function() {
console.log(num);
}
}
var f = Fun();
f();
</script>
</body>
</html>
(2)
闭包2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,
// 因此可以把闭包简单理解成“定义在一个函数内部的函数”。
// 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
// 闭包的最大用处有两个,一个是可以读取函数内部的变量,
// 另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
function createIncrementor(start) {
return function () {
return start++;
}
}
var inc = createIncrementor(5);
console.log(inc()); //5
console.log(inc()); //6
console.log(inc()); //7 //通过闭包,延长了内部变量start的生命周期
// 上面代码中,start是函数createIncrementor的内部变量。
// 通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。
// 为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于createIncrementor,
// 因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
</script>
<script>
// 闭包的另一个用处,是封装对象的私有属性和私有方法。
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
setAge: setAge,
getAge: getAge,
}
}
var p1 = Person('ZhangSan');
p1.setAge(25);
console.log(p1.getAge());
// 上面代码中,函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。
// 注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,
// 所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
</script>
</body>
</html>
(3)
闭包案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul class="test">
<li>hello</li>
<li>world</li>
<li>love</li>
<li>you</li>
</ul>
<script>
// 1. 利用闭包的形式得到当前小li的索引号
var lis = document.querySelector('.test').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 创建4个立即执行函数
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i);
}
// 2. 打车案例
console.log('----------');
var car = (function () { // 该函数就是闭包
var start = 13;
var total = 0;
return {
price: function (n) {
if (n <= 3) {
total = start;
} else {
total = start + (n - 3) * 5;
}
return total;
},
yongdu: function (flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5));
console.log(car.yongdu(false));
console.log(car.yongdu(true));
</script>
</body>
</html>
2.10
全局函数
(1)
eval()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--
1. eval() 函数会将传入的字符串当做 JavaScript 代码进行执行;
2. eval()是一个全局的函数;
语法:eval(string);
string: 一个表示JavaScript表达式、语句或一系列语句的字符串。表达式可以包含变量与已存在对象的属性;
返回值:返回字符串中代码的返回值。如果返回值为空,则返回 undefined;
3. 永远不要使用eval()!!! 它是一个危险的函数!!!
-->
</head>
<body>
<script>
/*
1. 将字符串当做语句执行;
eval('var a = 1;');
console.log(a); //上面的代码将字符串当做语句执行,生成了变量a
*/
eval('var a = 1;');
console.log(a); // 1
/*
2. eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题;
eval('a = 5'); // 此时,已经修改了外部变量a的值,由于这个原因,eval()有风险
console.log(a);
*/
eval('a = 5');
console.log(a); // 5
/*
3. 为了防止这种风险,JavaScript规定,如果使用严格模式,eval内部声明的变量,不会影响到外部作用域;
(function(){
'use strict';
eval('var foo = 456;');
// console.log(foo); // error => foo is not defined
})();
上面代码中,函数f内部是严格模式,这时eval内部声明的foo变量,就不会影响到外部;
不过,即使在严格模式下,eval依然可以读写当前作用域的变量;
(function() {
'use strict';
var foo = 1;
eval('foo = 2; ');
console.log(foo); // 2
})();
上面代码中,严格模式下,eval内部还是改写了外部变量,可见安全风险依然存在;
此外,eval的命令字符串不会得到 JavaScript 引擎的优化,运行速度较慢。这也是一个不应该使用它的理由;
*/
</script>
</body>
</html>
(2)
isNan()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
1. isNan(): 用来判断一个值是否为NaN(not a number);
*/
console.log(isNaN(NaN)); // true
console.log(isNaN(123)); // false
console.log(isNaN({})); // true
console.log(isNaN(['xyz'])); // true
console.log(isNaN([])); // false
console.log(isNaN([123])); // false
console.log(isNaN(['123'])); // false
</script>
</body>
</html>
(3)
parseInt()/Float()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
1. parseInt(string): 将字符串转为整数;
基本用法:
1. 如果字符串头部有空格,空格会被自动去除;
2. 如果parseInt的参数不是字符串,则会先转为字符串再转换;
3. 字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,
就不再进行下去,返回已经转好的部分;
4. 如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN;
5. 所以,parseInt的返回值只有两种可能,要么是一个十进制整数,要么是NaN;
6. 如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析;
7. 如果字符串以0开头,将其按照10进制解析;
进制转换:
1.parseInt方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数;
默认情况下,parseInt的第二个参数为10,即默认是十进制转十进制;
*/
console.log(parseInt('1000', 10)); // 1000
console.log(parseInt('1000', 2)); // 8
console.log(parseInt('1000', 6)); // 216
console.log(parseInt('1000', 8)); // 512
/*
2. parseFloat(): 将一个字符串转为浮点数;
基本用法:
1. 如果字符串符合科学计数法,则会进行相应的转换;
2. 如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分;
3. parseFloat方法会自动过滤字符串前导的空格;
4. 如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回NaN;
5. 尤其值得注意,parseFloat会将空字符串转为NaN;
*/
</script>
</body>
</html>
3. 面向对象案例完善
https://gitee.com/xingweicoding/studys