01、javascrip浏览器发展史
1.js的逼格(特点):
1.解释性语言
(不需要编译成文件直接执行)跨平台
2.单线程:同一时间只能做一件事——js 引擎是单线程
同一时间做很多事叫(多线程)
同步:计算机所说的同步是现实中的异步执行
异步:计算机所说的异步是现实中的同步执行
3.ECMA标注:
JavaScript
ECMAScript WebAPIs
DOM BOM
- js 是解释性语言:(不需要编译成文件)跨平台
- java 先通过 javac,编译成.class 文件,通过 jvm(Java 虚拟机)进行解释执行
.java→javac→编译→.class→jvm→解释执行(java 可以跨平台)(java 是 oak 语言) - link rel = “”是异步加载
- 单线程:同一时间只能做一件事——js 引擎是单线程
(同一时间做很多事叫多线程)
2.浏览器的组成
1.shell部分
2.内核部分
>渲染引擎(语法规则和渲染)
>JS引擎
>其他模块
3.主流浏览器
浏览器 内核
IE trident
chrome webkit/blink
Firefox Gecko
opera presto
Safari webkit
02、 JS介绍入门,JS引入,变量,值类型,运算符
1.JS引入方法
1.页面JS(页面嵌套<script></script>标签)
<script type="text/javascript">
document.write('hello world!!!')
</script>
小技巧:
(把type属性改为text/tpl故意让js运行不了把其当做一个库存)
<script type="text/tpl">
document.write('hello world!!')
</script>
2.外部JS(外部引入<script src=“地址”></script>)
<script type="text/javascript" src="lesson6.js"></script>
lesson6.js
document.write('hello world')
为了符合web标注(w3c标准中的一项)结构、样式、行为相分离,通常会采用外部引入
解释:
结构 样式 行为 相分离
html css js
2.js基本语法
1.变量(存放数据,以便后续使用)
>变量声明
<script type="text/javascript">
var b //变量声明
var a = 100
</script>
>声明多个变量(单一var模式)
<script type="text/javascript">
var a = 10,
b = 20,
c = 30,
d = 40,
e
</script>
命名规则
1.变量名必须以英文字母、_、$开头
2.变量名可以包括英文字母、_、$、数字
3.不可以用系统的关键字(有特殊语法含义)、保留字(未来可能会有含义的)作为变量名
下面是变量,例:
var a = 10;
var b = 20;
var c;
c = a + b;
先运算等号右边的 a+b,运算完后,再赋值给左边 c
先取值,再赋值
2.基本语法
值类型——数据类型
>不可改变的原始值(栈数据)
Number String Boolean undefined(声明一个变量没赋值) null(占位用)
> 引用值
array 数组, Object, function … data,RegExp 正则
var arr = [1,2,3,4,5,false,”abc”]; //这是数组
//例:
var arr = [1];
var arr1 = arr;
arr.push(2);
document.write(arr1);
//答案:arr 是 1,2 arr1 是 1,2
js 由值决定类型
var a = 10;var b =a;是 a 先取出 10,copy 一份放到 b 里面,改变 a 的值,b 的值是不变的,再把 a=20;时 b 的值还是 10,不发生改变。
var arr = [1,2];
var arr1 =arr;
arr.push(3);
//答案:这往[1,2]放 3,arr 和 arr1 都是[1,2,3]
引用值是在栈内存里面放堆的地址,拷贝的也是地址,所以改变 arr,arr1 也变了
var arr = [1,2];
var arr1 =arr;
arr = [1,3];
document.write(arr1)
//答案:arr = [1,3];
新建了一个新的房间。arr1 是 1,2,现在是插入新引入值”房间”,会在堆里面重新申请一间房,并指向新房间
3.js 语句基本规
- 语句后面要用分号结束“;”但 function test(){},for(){},if(){}后面都不用加分号
- js 语法错误会引发后续代码终止,但不会影响其它 js 代码块
- 书写格式要规范,“= + / -”两边都应该有空格
错误分为两种
1)低级错误(语法解析错误),不能写中文
2)逻辑错误(标准错误,情有可原,错的那个执行不了)
4.js运算符
运算操作符
1. +
-
“+”作用:数学运算、字符串链接
-
任何数据类型加字符串都等于字符串
例 var a = “a”+ true + 1; //打印 atrue1
例 var a = 1 + “a” + 1 + 1; //打印 1a11
例 var a = 1 + 1 + “a” + 1 + 1; //打印 2a11,从左向右运算
例 var a = 1 + 1 + “a” +( 1 + 2); //打印 2a3
例 var a = 0 – 1; //等于-1
例 var a = 2 * 1; //等于 2
例 var a = 0 / 0; //答案是 NaN,应该得出一个数字类型的数,但是没法表达,
就用 NaN (NaN 是 Not a Number 非数,不是数,但是是数字类型
例 var a = 1 / 0; //是 infinity
例 var a = -1 / 0; /是-infinity
摩尔,模,是取余数的意思
例 var a =5%2 //5%2 是五除二的余数,商二余一
例 var a =5%1 //是五除一的余数,结果是 0
例 var num = 1 % 5; //意思是 1 除以 5 的余数。商 0 余 1
例 var a =4%6 //是四除六的余数,结果是 4
例 var a = 4;a % = 5;document.write(a); // 4
例 var a = 0;a % = 5;document.write(a); //0
例 var a = 10;a %= 2;document.write(a); //0
例 var a = 3;a % = 4; //4
优先级”=“最弱【赋值符号优先级最低】,”()”优先级较高
例 var a = 10; a = a + 1; //结果 11
例 var a = 1;
a = a + 1;写成 a ++是一种简化形式“++”,是自身加一,再赋值给自身
a++是 a=a+1 的简化形式
例 var a =10;document.write(++a );document.write(a); //答案 11;11
是先执行++,再执行本条语句 document.write(++a)
例 var a =1;document.write(a ++);document.write(a); //答案 1;2。是先执行
语句(document.write(a)),再++,所以第一次打印的还是 a,第二次打印 a++后的值
例 var a =10;var b=++a -1+a++;document.write(b + “ ” + a) //答案 21 12
先++a,这个时候 a=11,再-1,再加 a,b 就是 21,最后++,a 就是 12
赋值的顺序自右向左,计算的顺序自左向右(按数学来)
例 var a =1;var b = a ++ + 1;document.write(b); //答案 2,先执行 var b =a+1,
再 a++
例 var a =1;var b = a ++ + 1;document.write(a);document.write(b); //答案 2,2
例 var a =1;var b = ++a + 1;document.write(a);document.write(b); //答案 2,3
例 var i = 1;var a = i++; //答案 a = 1; 此时 i 先将值 1 赋给 a,然后自己+1,i=2;
var b = ++i; //答案 b = 3;此时 i 先自己+1 为 3.再给 b 赋值,b=3;
“- -”,是自身减一,在赋值给自身
例 var a = 1;var b = a-- + -- a;document.write(b); //答案 0,先执行--a;此时 a 变成
0,然后第一个 a 也变成 0,那么 b = 0-- + --a
例 var a = 1;var b = --a + --a;document.write(b); //答案-1
例 var a = 1;document.write(a++);document.write(a); //答案 1;2
例 var a = 1;document.write(++a);document.write(a); //答案 2;2
例 var a =1; var b = a ++ +1;document.write(b); //答案 2
a 写在后面就后运行,先计算 a+1=2 赋值给 b 后再++
例 var a = 1;var b= ++a + 1;document.write(a);document.write(b); //答案 2;3
例 var a =10;a ++;a ++;a ++;加十个
简化写法:a +=10;也是 a = a+10;
例 var a =10;a += 10 + 1; //答案 21
例 var a = 1;a = a + 10;等于 a+=10
a++是 a +=1 的写法
例 var a=10;a/=2; //答案 5,是除二赋给自身的意思
例 var a =10;a *=2; //答案:20,是乘二赋给自身的意思
例 var a=10;a%=2; //答案:0, 10 能整除 2,余数是 0,取余,余数赋给自身。
例 var a=3;a%=4; //答案:3,3 除以 4,余数为 3,余数赋给自身。
例 var a=0;a%=4; //答案:0,0 除以 4,余数为 0,余数赋给自身。
例 var a = 1;a% =10; //答案:1,1 除以 10,余数为 1,余数赋给自身。
法一:
var a = 123;
var b = 234;
var t;
t = a;
a = b;
b = t;
法二:
var a = 123;
var b = 234;
a = a + b;
b = a - b;
a = a - b;
03、比较运算符,逻辑运算符
1. 比较运算符
1、“>”,”<”,”==”,“>=”,“<=”,”!=”比较结果为 boolean 值
但凡是运算符,都是要有运算的
用到布尔值,true 或 false
字符串的比较,比的是 ASCII 码(七位二进制 0000000)
1.>, <
例 var a = "a">"b";document.write(a); //答案是 false
例 var a = 1 > 2;document.write(a); //答案是 false
例 var a = 1 < 2;document.write(a); //答案是 true
例 var a = "1">"8";document.write(a); //答案是 false
例 var a = "10">"8";document.write(a); //答案 false,不是十和八比,是字符串一
零和八比,先用开头的一和八比,比不过就不看第二位了;一样的就拿零和八比
例 var a = 123;document.write(a); //答案 false
运算结果为真实的值
2. = =,等不等于
例 var a = 1 == 2; //答案是说 1 等不等于 2,因为 1 肯定不等于 2,所以值为 false
例 var a = NaN == NaN; //答案是 false,NaN 不等于任何东西,包括他自己
例 var a = undefined == underfined; //答案是 true
例 var a = infinity == infinity; //答案是 true
例 var a = NaN == NaN; //答案是 false。非数 NaN 是不等于自己的
NaN 得不出数,又是数字类型,就是 NaN
3. >=,<=,!=是否不等于,非等
比较结果为 boolean 值:true 和 false
4. “&&”与运算符
两个表达式:先看第一个表达式转换成布尔值的结果是否为真,如果结果为真,那么它会看第二个表达式转换为布尔值的结果,然后如果只有两个表达式的话,只看第二个表达式,就可以返回该表达式的值了,如果第一位布尔值为 false,不看后面的,返回第一个表达式的值就可以了
例 var a = 1 && 2; //答案 2,如果第一位 1 为真,结果就为第二位的值 2
例 var a = 1 && 2 + 2; //答案 4,如果 1 为真,结果就为 4
例 var a = 0 && 2 + 2; //答案 0
例 var a = 1 && 1 && 8; //答案 8,先看第一个是否为真,为真再看第二个,
中途如果遇到 false,那就返回 false 的值
例 var a =1 + 1 && 1 – 1;document.write(a); //答案 0
如果是三个或多个表达式,会先看第一个表达式是否为真,如果为真,就看第二个
表达式,如果第二个也为真,就看第三个表达式(如果为真就往后看,一旦遇到假
就返回到假的值),如果第三个是最后一个表达式,那就直接返回第三个的结果
如果第一个是假,就返回第一个值,当是真的时候就往后走,一旦遇到假,就返回
例:2>1 && document.write(‘成哥很帅’) //意思是如果 2 大于 1,那么就打印成哥很帅,如果前面真才能执行后面的(相当于短路语句使用)
&&与运算符是有中断作用的,当短路语句使用(如果。。那么。。)
例 var data = ...; //执行一个语句,会用到 data
data&&执行一个语句全用到 data
例 data && function(data);
5.&与运算 我们一般不用
上下一与不同为0,相同为1 | ||
---|---|---|
1在二进制中是1,(为了对齐补的0) | 0 | 1 |
3在二进制中是11 | 1 | 1 |
运算结果 | 0 | 1 |
例 var num = 1 & 2;document.write(num); //答案 0
例 var num = 1 & 1;document.write(num); //答案 1
例 var num = 1 & 3;document.write(num); //答案 1
6. “||”或运算符
看第一个表达式是否为真,如果为真,则返回第一个值,碰到真就返回
如果第一个表达式是假,就看第二个表达式,如果第二个是最后一个,就返回第二个的值
例 var num = 1 || 3; //答案 1
例 var num = 0 || 3; //答案 3
例 var num = 0 || false; //答案是 false
关于真假的说法:全假才为假,有一个真就为真
例 var num = 0 || false || 1;document.write(num); //答案 1
例 div .onclick = function(e){
非 IE 浏览器直接取 e 值
var event = e;
IE 浏览器存在 window.event;
}
写成下面这样就解决了兼容性。在所有的浏览器中都好使
div .onclick = function(e){var event = e || window.event;}
7.“!“非运算符,否的意思。
先转成布尔值,再取反
例 var a = ! 123;document.write(a); //答案 false。123 的布尔值是 true,取反是 false
例 var a = ! “”;document.write(a); //答案 true。空串””布尔值是 false,取反是 true
例 var a = ! !“”;document.write(a); //答案 false,取反后,再反过来,结果不变
例 var a = true;a =!a;document.write(a) //答案 false,自身取反,再赋值给自身
被认定为 false 的值:转换为布尔值会被认定为 false 的值 undefined,null,NaN,
“”(空串), 0, false
04、条件语句和循环语句
1. If 语句 与&&
if(条件判断){
当条件成立时,执行里面的执行语句
}
当 if 的条件成立时,才能执行{}内的语句
当条件转化为布尔值,如果为 true 就执行;如果为 false 就不执行
例 if ( 1 > 0 && 8 > 9){}
&&放在 if 中的,全真才为真,&&是并且的意思
例 if ( 1 > 0 || 8 > 9){}
||放在 if 中是或者的意思,有一个是真就可以了
2.IF 和&&的互相转化
if (1 > 2) {
document.write.(‘a’);
}
上面与右边效果完全一样 1 > 2 && document.write(‘a’);
&&和 II 比较常用在条件判断中
3.for 循环(for 循环不固定,非常灵活)
格式 for (var i = 0; i < 10; i++) {}
for 是关键字,()括号里面三个语句用两个分号隔开,{}里面是循环体
打印十个a写成:
法一:
for(var i = 0; i<10 ; i++){
console.log('a');
}
执行顺序如下:
(1)var i= 0;
(2)if(i <10){
console.log(‘a’)
}
法二:
var i=0;
for(;i<10;){
console.log('a');
i++;
}
法三:
var i=1;
var count = 0;
for(; i ; ){
console.log('a');
count++;
if(count == 10){
i=0;
}
}
法四:
var i = 1;
for( ; i ; ){
console.log('a');
i++;
if(i==11){
i=0;
}
}
把条件判断放到 if 里面,条件判断成立,就执行{}中间的执行体
4.while, do while
- while 循环是 for 循环的简化版 for( ; ; ){},while 循环底层机制是 for 循环。
for ( ; 只在这一部分写,前后不写 ; ){} - do while 是不管满不满足条件都会先执行一次,再判断成不成立,如果成立才会执行第二次,不成立就停止 一般没人用
do{
document.write('a');
i ++;
}while(i < 10)
5.作业
1.
let n = window.prompt('input');
let result = 1;
for (let i = 1; i <= n; i++) {
result *= 2;
}
document.write(result);
2.
let n = window.prompt('input');
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i;
}
document.write(result)
3.
法一:
let n = parseInt(window.prompt('input'))
let arr = [1, 1];
for (let i = 1; i < n; i++) {
arr[i + 1] = arr[i] + arr[i - 1]
}
document.write(arr[n - 1])
法二:
let n = parseInt(window.prompt('input'))
let f = 1,
s = 1,
t;
if (n > 2) {
for (i = 2; i < n; i++) {
t = f + s;
f = s;
s = t;
}
document.write(t);
}
else {
document.write('1')
}
4.
let n = parseInt(window.prompt('请输入一个三位数的正整数'));
while (n > 0) {
let t = parseInt(n % 10);
document.write(t);
n = parseInt(n / 10);
}
5.
let a = +window.prompt('a');
let b = +window.prompt('b');
let c = +window.prompt('c');
let max;
if (a > b) {
if (a > c) {
max = a;
} else {
max = c;
}
} else {
if (b > c) {
max = b;
} else {
max = c;
}
}
document.write(max)
6.
let count = 0;
for (let i = 1; i < 100; i++) {
for (let n = 1; n <= i; n++) {
if (i % n == 0) {
count++;
}
}
if (count == 2) {
document.write(i + ' ');
}
count = 0;
}
6.switch case 条件判断语句
if(条件判断)
switch(条件){
case 写条件:里面判是否相符:
如果相符合就执行 case 后面的语句比如 console.log(‘a’)
}
switch 不负责任,如果判断了 a 是符合条件的,也会把后面的连带打印出来(穿透)
加个 break,就可以终止语句
7.break
var i = 0;
while(1){
i++;
console.log(i)
if(i>100){
break;
}
}
break终止的是while,对if没有影响(break只能终止循环)
8.continue 继续
终止本次循环,后面的都不执行了,来进行下一次的循环
js 里面是没有 goto 的,c 语言里面有
例当 i 是 7 的倍数,或尾数是 7 的时候,不打印
for ( var i = 0; i < 100; i++){
if( i % 7 == 0 || i % 10 == 7){
}else{
console.log(i);
}
}
下面的写法更好
for ( var i = 0; i < 100; i++){
if( i % 7 == 0 || i % 10 == 7){
continue;
}
console.log(i)
}
05.初始引用值
1.数组
例 var arr = [1,2,3,4,5,6,7,”abc”,undefined];
arr [0] 代表查数组的第一位,因为计算机是从 0 开始的算的
arr [0] = 3; 是指把数组的第一位改成 3,显示 3,2,3,4,5,6,7, ”abc”,undefined
arr.length;是数组的长度,有多少位就有多少
console.log(arr.length); //答案是 8 位
例:利用 for 循环把数组中的每一位都拿出来——遍历
var arr = [1,2,3,4,5,6,7,”abc”,undefined];
for (var i = 0 ; i < arr.length ; i++){
console.log(arr[i])
}
例:把数组中的每一位都改成 1
var arr = [1,2,3,4,5,6,7,”abc”,undefined];
for ( var i = 0 ; i < arr.length ; i++){
arr[i]=1;
}
例:把数组中的每一位都加 1
var arr = [ 1 , 2 , 3 , 4 , 5 ,6 , 7]
for (var i = 0 ; i < arr.length ; i++){
arr[i]+=1
}
2. 对象 object
1.面向对象的编程方法
var obj = {
里面存属性和方法
key 属性名:value 属性值;
}
1在{}面用。属性与属性之间用逗号隔开
2属性值可以双引号或单引号;属性名是为了方便找到他,只是一个辅助
06、编程形式的区别
1.面向过程,如 c
第一步干嘛,第二步干嘛
2.面向对象(对象 object)
现在 js 是一半面向过程,一半面向对象,前面学的都是面向过程
07、typeof 操作符
typeof 能返回的六种数据类型(区分数字类型)
number、string、boolean、undefined、object、function
例 var num = 123;console.log(typeof(num)); //返回 number
写成 console.log(typeof num );//也可以不过最好加括号
例 var num = {}; console.log(typeof(num)); //泛泛的引入值都返回 object
例 var num = [];console.log(typeof(num)); //泛泛的引入值都返回 object
例 var num = null;console.log(typeof(num)); //答案 null 返回 object,最早是代替空对象的
例 var num = undefined;console.log(typeof(num)); //答案返回 undefined
例 var num = fuction(){};console.log(typeof(num)); // 答案返回 function
typeof 返回的 值是string类型
08、类型转换
例 var num = 1 +“1”; //显示 11
例 var num = 1 *“1”;console.log(typeof(num) + “:” + num); //显示 number:1
例 var num = 1 -“1”;console.log(typeof(num) + “:” + num); //显示 number:0
例 var num = “2” -“1”;console.log(typeof(num) + “:” + num); //显示 number:1
例 var num = “2” *“1”;console.log(typeof(num) + “:” + num); //显示 number:2
以上例子说明 js 有类型转换
1. 显示类型转换
1.Number(mix) 是想把里面的东西转换成数字
例 var num = Number(‘123’);
console.log(typeof(num) + “:” + num);
答案显示 Number:123,把字符串类型的 123 转换成了 number 类型
例 var demo = “123”;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:123,上面那一行的 Number 是为了把()里面转换成数字类型
例 var demo = true;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:1
例 var demo = false;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:0
例 var demo = null;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:0
例 var demo = undefined;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:NaN
例 var demo = “abc”;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:NaN
例 var demo = “-123”;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:-123
例 var demo = “123abc”;
var num = Number(demo);
console.log(typeof(num) + “:” + num);
答案显示 Number:NaN
2.parseInt(string,radix)
parse 是转化,Int 是整型,整数,目的是把里面转换成整数
例 var demo =”123”;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number:123
例 var demo = true;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number: NaN
例 var demo = false;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number: NaN
例 var demo = 123.9;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 123,此处是直接去掉小数,不是四舍五入
例 var demo = “10”;
var num = parseInt(demo ,16);
console.log(typeof(num) + “:” + num);
答案显示 number: 16
var num = parseInt(demo ,radix); //radix 是基底的意思
radix 写成 16,系统会认为是以 16 进制为基底, 10(一零)是 16 进制的一零,是
以 16 进制为基底,把他转成为 10 进制的数字(就是 16),上面是以目标进制为基底,
转换成十进制(radix 范围是 2-36)
例 var demo = “3”;
var num = parseInt(demo ,2);
console.log(typeof(num) + “:” + num);
答案显示 number: NaN
例 var demo = “b”;
var num = parseInt(demo ,16);
console.log(typeof(num) + “:” + num);
答案显示 number: 11
例 var demo = “123abc”;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 123
例 var demo = “100px”;
var num = parseInt(demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 100
parseInt 从数字类开始看,看到非数字类为止,返回原来的数
3.parseFloat(string)
parseFloat(string)转换成浮点数字,就是正常小数
例 var demo = “100.2”;
var num = parseFloat (demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 100.2
例 var demo = “100.2.3”;
var num = parseFloat (demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 100.2
例 var demo = “100.2abc”;
var num = parseFloat (demo);
console.log(typeof(num) + “:” + num);
答案显示 number: 100.2
parseFloat 从数字类开始看,看到除了第一个点以外的非数字类为截止,返回前面的数
4.toString
例 var demo = 123;
var num = demo.toString();
console.log(typeof(num) + “:” + num);
答案显示 string: 123。相当于把 123 转换字符串。
想把谁转换成字符串,就写成谁.toString,上面是想把 demo 转换成 toString,写成
demo.toStringdemo.toString(radix)可以把10进制转换成目标进制
例 var demo = undefined;
var num = demo.toString();
console.log(typeof(num) + “:” + num);
答案显示报错,undefined 和 null 不能用 toString
例 var demo = 123;
var num = demo.toString(8);
console.log(typeof(num) + “:” + num);
答案 173,把 123 转成为八进制
这里的 radix 意思是以十进制为基底,转换成目标进制(即 8 进制)
例 var demo = 10;
var num = demo.toString(8);
console.log(typeof(num) + “:” + num);
答案 12
例 var demo = 20;
var num = demo.toString(8);
console.log(typeof(num) + “:” + num);
答案 24。以十进制为基底,把 20 转换成 8 进制,就是 24
例给你一个二进制的数,转换成十六进制,是先从二进制到十进制再到十六进制
var num = 10101010;
var test = parseInt(num, 2);
console.log(test.toString(16));
答案 aa
例 var num = 10000;
var test = parseInt(num, 2);
console.log(test.toString(16));
答案 10
undefined 和 null 不能用 toString
5.String(mix)
String(mix)转换成字符串,写什么都成了字符串
例 var demo = 123.234;
var num = String (demo);
console.log(typeof(num) + “:” + num);
答案显示 string: 123.234
例 var demo = undefined;
var num = String (demo);
console.log(typeof(num) + “:” + num);
答案显示 string: undefined
6.Boolean()
Boolean()转换成布尔值 false 和 true
例 var demo = “”;
var num = String (demo);
console.log(typeof(num) + “:” + num);
答案显示 boolean: false
2. 隐式类型转换
- 隐式类型转换是跟你转换了也不知道
- 隐式类型转换内部隐式调用的是显示的方法
- 隐式类型转换包括 isNaN () ,++,–, +/-(一元正负),+,*,% ,,&&,|| ,!,
<,>,<= ,>= ,== ,!=
1. isNaN ();
isNaN ();当你把一个数放到()里,它能判断是不是 NaN,先比括号里面的放到 number里面转换,然后返回来
例 console.log(isNaN(NaN); //答案 true
例 console.log(isNaN(“123”); //答案 false
例 console.log(isNaN(“abc”); //答案 true。会调用 number,先把“abc”
放 number 里面转换,通过 number 的转换再和 NaN 比对,如果相等就是 true
例 console.log(isNaN(null); //答案 false,在 number 里面放 null 是 0,不是 NaN
例 console.log(isNaN(undefined); //答案 true
2.++/–(加加减减) +/-(一元正负)
例 var a = “123”;
a ++;
答案 124,++这个符号放到这里,还没运算之前,先把前面的 a 转换成 number 的 123
例 var a = “abc”;
a ++;
答案 NaN
+/-(一元正负)
+a;-a;正 a 和负 a 都会变换成数字
例 var a =+”abc”;
console.log(a + “:” + typeof(a));
答案 NaN:number。
尽管转换不成数字,也会转换成数字类型,因为里面隐式的调用
了一个 number
3. +
+隐式类型会转换成 string,当加号两侧有一个是字符串,就用调用 string,把两个都变成字符串
例 var a = “a” +1
console.log(a + “:” + typeof(a));
答案:a1:string
4. * %
*和% 乘和模都会转换成 number.
例 var a = “1” *1; console.log(a + “:” + typeof(a));
答案 1:number
例 var a = “a” *1; console.log(a + “:” + typeof(a));
答案 1:number,先是 number(“a”)的结果乘以 number(1)的结果,最后是 NaN*1,
还是 NaN,但是数据类型是 number
5. && || !
与或非,都是有类型转换的,不过是返回的是表达式的值,不是隐士类型转换的值,但是判断是一个类型转换的值
6. < > <= >=
例 var a =1 > “2”; console.log(a + “:” + typeof(a));
答案 false:boolean,有数字相比较的,就会隐士类型转换成数字类型
例 var a =”3” > “2”; console.log(a + “:” + typeof(a));
答案这个没类型转换,这个比的是 ASCⅡ
例 var a =”3” > 2; console.log(a + “:” + typeof(a));
答案 true:boolean 会转换成数字,因为数字优先
7.== !=
例 var a = 1 == “1”; console.log(a + “:” + typeof(a));
答案 true:boolean,也有隐士类型转换
例 var a = 1 == true; console.log(a + “:” + typeof(a));
答案相等
!=也是这样
特殊的东西在控制台操作
例 false>true //答案 false,会先转换成数字,0>1 当然是错的
例 2>1>3 //答案 false
例 2>3<1 //答案 true
例 10>100>0 //答案 false
例 100>10>0 //答案 true
以上都是挨个算的,先看前面的是 true 还是 false,再和后面的比,不是顺着下来
例 undefined>0 //答案 false
例 undefined==0 //答案 false
例 undefined<0 //答案 false
例 null>0 //答案 false
例 null==0 //答案 false
例 null<0 //答案 false
例 undefined == null //答案 true
例 NaN ==NaN //答案 false,NaN 是唯一一个连自己都不等于的
3. 不发生类型转换
1.绝对等于(三个等号) !==绝对不等于
例 1 === 1 //答案 true
例 1 === “1” //答案 false
例 1 !== “1” //答案 true
例 1 !== 1 //答案 false
例 NaN =NaN //答案 false 特殊的
例 console.log(a); //如果定量没定义就直接访问,就 a is not defined 报错;
有一种特殊情况,当且仅当把未定义的变量放到 console.log(typeof(a));里面就访问,
不报错,返回 undefined
例 console.log(typeof(a)); //答案 undefined,这个 undefined 是字符串
例 console.log(typeof(typeof(a))); //答案 string。console.log(typeof(typeof(a)));
可以先解析成 console.log(typeof(“undefined”));再返回一次就是 string 字符串
上面考的是 typeof(a)返回的六种类型的值(number、string、boolean、undefined、object、function)都是 undefined 字符串
4.作业
alert(typeof (a));//string
alert(typeof (undefined));//undefined
alert(typeof (NaN));//number
X alert(typeof (null));//object
alert(typeof (NaN));//number
var a = "123abc";
alert(typeof (+a));//number
X alert(typeof (!!a));//boolean
alert(typeof (a + ""));//string
alert(1 == "1");//true
alert(NaN == NaN);//false
alert(NaN == undefined); //false
alert("11" + 11); //1111
alert(1 === "1");//false
alert(parselnt("123abc"));//123【parseInt 是截断数字】
var num = 123123.345789;
alert(num.toFixed(3));//123123.346【toFixed(3)是保留三位小数的意思,四舍五入】
typeof (typeof (a));//string
09. 函数function
function 随便起个名(){}
以上情况就是偶合,偶合度非常高,偶合代码就是低效代码
编程讲究高内聚,弱偶合
右上方是简便写法:可以用 test 调用执行,写几个 test 就调用执行几次
function text() {
var a = 123;
var b = 234;
var c = a+ b;
console.log(c)
}
test()
答案 357。写了一个 test();就执行了一遍函数语句,如果不写 test();就相当于有一个框来存东西,但是不执行
1.定义
定义一个函数可以先写一个 function,函数就是另一个类型的变量
我声明一个函数 test,test 是函数名。写成下面
function test(){
函数体
}
例 function theFirstName(){}
document.write(theFirstName);
答案 function theFirstName(){}。打印出来的是函数体
这与 c 语言和 c++,他们打印指针,会输出指针的地址,而 js 这种弱数据语言(解
释性语言)永远不输出地址,输出地址指向房间
函数名起名:开发规范要求,函数名和变量名如果由多个单词拼接,必须符合小驼
峰原则(第一个单词首字母小写,后面的首字母大写)
2.函数表达式
例 var test = fuction test (){
document.write(‘a’);
}
test();
答案 a。这种方式像定义一个变量
上面这种方式,可以演变成第三种,匿名表达式【不写 test 这种函数名】
例 var demo = fuction (){
document.write(‘a’);
}
(1)命名函数表达式
var test = function abc() {
console.log('a');
}
上面这个函数的函数名 name 是 abc
在控制台 console 直接输出 test 就会出现
在控制台 console 直接输出 abc 会报错,表达式就会忽略他的名字 abc。
在上面例子中,fuction abc(){document.write(‘a’);}这一部分叫表达式,是会忽略
abc 这个地方的名字,会变成匿名函数表达式,不如直接写成匿名函数
(2)匿名函数表达式(常用,一般说的函数表达式就是匿名函数表达式)
function test() {}
test.name 输出 test
3. 组成形式
1、函数名称
function test(){}其中 function 是函数关键字,test 是函数名,必须有(){},参数可有可
没有,参数是写在()括号里面的。
如果写成 function test(a,b){},相当于隐式的在函数里面 var a,var b 申明了两个变
量,()括号里面不能直接写 var
例 function test(a, b){
document.write(a + b)
}
test(1, 2)
答案 3。上面这个例子,1 就会到 a 里面去,2 就会到 b 里面去,这是传参的形式
2、参数(可有可没有,但是高级编程必有)
(1)形参(形式参数):指的是 function sum(a,b){}括号里面的 a 和 b
(2)实参(实际参数):指的是 sum(1,2);里面的 1,2
天生不定参,形参可以比实参多,实参也可以比形参多
function sum(a,b){
var c = a + b;
console.log(c)
};
sum(1,2)
sum(3,4)
答案 3 7,参数把函数抽象了,可以组成很多形
例 function sum(a,b){
document.write(a);
}
sum(11, 2, 3)
答案 11
例 function test(a, b, c,d) {
document.write(a);
document.write(d);
}
sum(11, 2, 3)
答案 11,undefined,上面这道题是形参多,实参少
js 参数不限制位置,天生不定参数
在每一个函数里面都有一个隐式的东西 arguments 这个是实参列表
console.log();是把信息展示在控制台
document.write();是把信息展示到网页
例 function test(a) {
console.log(arguments);
console.log(arguments.length);
}
sum(11, 2, 3)
答案[11, 2, 3],3
例 function test(a) {
for(var i = 0; i < argument.length; i++){
console.log(arguments[i]);
}
}
sum(11, 2, 3)
答案 11,2,3
例:形参长度求法
function sum(a, b, c, d) {
console.log(sum.length);
}
sum(11, 2, 3)
答案 4
例任意个数求和(不定参才能求出来)
function sum () {
var result= 0;
for (var i = 0;i<arguments.length;i++)|
results + = arguments[i]
}
consoel.log(result)
}
sum(1,2,3,4,5,6,7,8,9)
形参永远有尽头,要实现任意的数求和,无法定义形参。
function sum (a,b){
//arguments [1 , 2 ]
a = 2 ;
console.log(arguments[0])
}
sum (1,2)
//答案是2,a变,arguments跟着变,有一个映射关系。
function sum (a,b){
//arguments [1, 2 ]
//var a = 1;
a= 2;
arguments[0] = 3 ;
console.log(a)
}
sum (1,2)
答案是3,arguments里面一个变,一个跟着变,但【1,2】是两个人的,相当于映射关系
例当形参两个,实参一个
function sum(a, b){
//arguments[1]没值
b = 2;
console.log(arguments[1]);
}
sum(1);
答案 undefined,实参列表出生时有几个,就有几个,在写 b=2,也不加在 arguments[1]
里面了,此处的 b 就当变量用,他和实参不映射。
4. 返回值 return
结束条件和返回值 return,return 有终止函数的功能
没写 return,实际上是加上了一个隐式的 return
例 function sum(a, b){
console.log(‘a’);
console.log(‘b’);
return;
}
答案 a,b
例 function sum(a, b){
console.log(‘a’);
return;
console.log(‘b’);
}
答案 a
return 最常用的是返回值。本意是把一个值返回到函数以外
自己定义的函数也能返回,return 空格 123
例 function sum(){
return 123;
console.log(‘a’)
}
var num = sum();
答案这里的 num 就是 123,而且 console.log(‘a’);无效,这里的 return 又终止函数,又返回变量
例把 target 转成数字
例把 target 转成数字
function myNumber(target){
return +target; //利用+隐式的转换成了数字类型
}
var num = myNumber(‘123’);
console.log(typeof(num) + “” + num);
答案 number 123
一般函数处理完一个参数,是为了返回
typeof()也是返回值,也是函数
typeof(123)也可以写成 typeof 123【typeof 空格 123】,只是看起来不方便
10.作用域初探
1.作用域定义
变量(变量作用于又称上下文)和函数生效(能被访问)的区域全局、局部变量
作用域的访问顺序
作用域:函数里面的可以访问外面的全局变量
函数外面不能用函数里面的。里面的可以访问外面的,外面的不能访问里面的,彼此独立的区间不能相互访问
例 function test(){
var a =123;
function demo(){
var b = 234;
document.write(a);
}
demo();
document.write(b);
}
上面的 document.write(b);不能访问 var b ,
上面的 document.write(a);可以访问 a
外层函数不能访问里层的,里层的可以访问外层的,越往里权限越大
2.js 运行三部曲
1 语法分析 → 2 预编译 → 3 解释执行
3.预编译前奏
例 function test(){
console.log(‘a’);
}
test();
上面能执行
例 test();
function test(){
console.log(‘a’);
}
也能执行,因为有预编译的存在
例 var a = 123;
console.log(a);
答案 123
例 console.log(a);
var a = 123;
答案 undefined
例只写 console.log(a);就会报错
函数声明整体提升:函数不管写到哪里,都会被提到逻辑的最前面。所以不管在哪里调用,本质上都是在后面调用
变量声明提升:把 var a 提升到最前面
var a = 123;这是变量声明再赋值。
变量 声明提升是把他拆分成 var a; a = 123;然后把 var a 提升到最前面
上面这两句话没办法解决下面例子的问题
例 function a(a){
var a = 234;
var a = function(){
}
a();
}
var a =123;
- imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象(就是 window)所有。
例 window.a = 10;
例 a = 10; ===> windows.a = 10;
- 一切声明的全局变量,全是 window 的属性。
例 var a = 123; ===> window.a = 123;
- window 就是全局的域
如果在全局变量在 var a = 123;那么就会返回到 window
例 var a = 123
console.log(a) ===> window.a
例 var a = b = 234;是把 234 的值赋给 b,在把 b 的值赋给 a
例 function test(){
var a = b = 123;
}
test()
写 test()代表执行 test,赋值是自右向左的,上面先把 123 赋给 b 的时候,b 未经声明,
然后再声明 a,再 b 的值赋给 a,导致 b 未经声明,所以 b 归 window 所有
访问 window.a 是 undefined,访问 window.b 是 123
例 function test(){
var b = 123;
}
test();
console.log(window.b);
答案 undefined
window 就是全局
例 var a = 123;
console.log(a); → console.log(window.a);
例 var a = 123;
var b = 234;
var c = 345;
window{
a : 123,
b : 234,
c : 345
}
如果 var a 对应会有 window.a
4.预编译(解决执行顺序问题)
例 function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a (){}
console.log(a);
var b = function (){}
console.log(b);
function d() {}
}
fn(1);
答案是 function a(){}//123//123//function (){}
这个例子的形参是(a),变量声明也是 a
上面的例子按四部曲变化如下:
找形参和变量声明,将变量和形参(a)名作为 AO 属性名,值为 undefined ,AO{
a : undefined,
b : undefined,
}
(把实参值传到形参里)AO{
a : 1
b : undefined,
}
function a () {}和 function d () {}都是函数声明,但是 var b = function (){}不是。AO{
a : function a () {},
b : undefined,
d : function d () {}
}
执行第一行 console.log(a);时,用的是 AO{
a : function a () {},
b : undefined,
d : function d () {}
}
执行 var a =123;改变的是 AO{
a : 123,
b : undefined,
d : function d () {}
}
在 b = function (){}时 AO{
a : 123,
b : function () {},
d : function d () {}
}
预编译发生在函数执行的前一刻
5.(函数)预编译的四部曲:
1.创建 AO 对象 Activation Object(执行期上下文,作用是理解的作用域,函数产生的执行空间库)
2.找形参和变量声明,将变量和形参名作为 AO 属性名,值为 undefined
相当于 AO{
a : undefined,
b : undefined
}
3.将实参值和形参统一(把实参值传到形参里)
4.在函数体里面找函数声明,值赋予函数体
(先看自己的 AO,再看全局的 GO)
例子 function test (a, b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b (){};
function d (){};
console.log(b);
}
test(1);
答题过程:找形参和变量声明,将变量和形参名作为 AO 属性名,值为 undefined, AO{
a : 1,
b : undefined,
c : undefined
}
函数声明 function b(){}和 function d(){},AO{
a : 1,
b : function b(){},
c : undefined,
d : function d(){}
}
执行 console.log(a);答案是 1
执行 c = 0;变 AO{
a : 1,
b : function b(){},
c : 0,
d : function d(){}
}
var c 不用管,因为 c 已经在 AO 里面了
执行 a = 3;改 AO{
a : 3,
b : function b(){},
c : 0,
d : function d(){}
}
执行 b = 2;改 AO{
a : 3,
b : 2,
c : 0,
d : function d(){}
}
执行 console.log(b);答案是 2
function b () {}和 function d(){}已经提过了,不用管
执行 console.log(b);答案是 2
例 function test(a , b){
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a (){}
var a;
b = 234;
var b = function(){}
console.log(a);
console.log(b);
}
test(1);
一旦有重名的,一但有 a 变量又有 a 函数【如 function a (){}】,又在第一条访问的是a,一定是函数
答题过程: 将变量和形参名作为 AO 属性名,AO{
a : undefined,
b : undefined
}
将实参值和形参统一,AO{
a : 1,
b : undefined
}
找函数声明 function a (){},AO{
a : function a (){},
b : undefined
}
执行 console.log(a);答案是 function a (){}
执行 console.log(b);答案是 undefined
执行 var b = 234;变 AO{
a : function a (){},
b : 234
}
执行 console.log(b);答案是 234
执行 a = 123;变 AO{
a : 123,
b : 234
}
执行 console.log(a);答案是 123
然后 function a (){};var a ;都可以不看了
执行 b = 234,b 值还是 234,不变
执行 var b = function (){},变 AO{
a : 123,
b : function (){}
}
执行 console.log(a);答案是 123
执行 console.log(b);答案是 function (){}
6.全局的预编译
例 console.log(a);
var a = 123;
答案 undefined
例 console.log(a);
var a = 123;
function a (){}
答案是打印 a 是 function a (){}
7.全局的预编译三部曲:
1、生成了一个 GO 的对象 Global Object(window 就是 GO)
2、找形参和变量声明,将变量和形参名作为 GO 属性名,值为 undefined
3、在函数体里面找函数声明,值赋予函数体
例 console.log(a);
var a = 123;
function a (){}
答案过程,GO{
a : undefined
}
函数声明 GO{
a : function a (){}
}
执行 var a = 123;变 GO{
a : 123
}
执行 console.log(a);就是 123
GO === window, GO 和 window 是一个东西
console.log(a);和 console.log(window.a);和 console.log(go.a);是一样
1.任何全局变量都是 window 上的属性
2.没有声明就是赋值了,归 window 所有,就是在 GO 里面预编译
例 function test(){
var a = b =123;
console.log(window.b);
}
test();
答案 a 是 undefined,b 是 123
先生成 GO{
b : 123
}
再有 AO{
a : undefined
}
先生成 GO 还是 AO?
想执行全局,先生成 GO,在执行 test 的前一刻生成 AO
在几层嵌套关系,近的优先,从近的到远的,有 AO 就看 AO,AO 没有才看 GO
例 console.log(test);
function test(test){
console.log(test);
var test = 234;
console.log(test);
function test(){
}
}
test(1);
var test = 123;
答题过程:想执行全局,先有 GO,GO{
test : undefined
}
发现有函数声明 GO{
test : function (){
..... }
}
执行 console.log(test)
,
执行 test(1)之前生成 AO{
test : function (){} }
执行 var test = 234;变成 234
AO 上面有就用 AO 的,没有就看 GO
的
例 var global = 100;
function fn(){
console.log(global); }
fn(); 答题过程 GO{
global : undefined,
fn : function(){.....} }
执行 var global = 100;
变 GO{
global : 100,
fn : function(){.....} }
不看 function fn(){...}里面的东西
执行 fn()之前 AO{
访问 GO
的 global
}
例子:
global = 100
function fn(){
console.log(global)
global=200
console.log(global)
var gloval = 300 ;
}
fn();
var global;
答题过程,GO{
global : undefined
fn : undefined(没用可以不写
)
}变 GO{
global : 100
fn : undefined }
执行 fn()之前,AO{
global : undefined }
执行结果是 undefined
,200
例子:
function test(){
console.log(b)//undefined
if(a){
var b = 100;
}
console.log(b) //undefined
c=234;
console.log(c)//234
}
var a ;
test()
//AO{
// b:undefined
//}
a=10;
console.log(c);//234
例现在在 if 里面定义函数声明 function 是不允许的,但是过去可以,下面就是过去的旧题,按可以来做
GO{
a : undefined
}
GO{
a : undefined
demo : function (){}
}
开始执行 a=100,GO{
a : 100
demo : function (){}
}
AO{
e : undefined,
b : undefined,
c : undefined,
a : undefined
}
形参实参相统一,AO{
e : 1,
b : undefined,
c : undefined,
a : undefined
}
赋值 AO{
e : function e (){},
b : undefined,
c : undefined,------旧规则里面可以提出 function(){}
a : undefined
}
执行 arguments[0] = 2;实参列表和传参是否相映射,变 AO{
e : 2,
b : undefined,
c : undefined,
a : undefined
}
执行 console.log(e);答案 2
if(a)由于 a 在 AO 里面是 undefined,所以不走 if
执行 a = 10;变 AO{
e : 2,
b : undefined,
c : undefined,
a : 10
}
执行 console.log(b),答案 undefined
执行 f = 123,变 GO{
a : 100,
demo : function (){},
f : 123
}
执行 console.log(c); 之前打印 function(){},改语法后打印 undefined
执行 console.log(a); 答案 10
执行 console.log(a); 因为在外面是全局的,答案 100
执行 console.log(a); 答案 123
答题//1 false+1 因为有+,两边都不是字符串,就转换成数字,false 是 0
//false false==1,false 肯定不等于 1,所以把 false 再赋给 demo
//undefined
typeof(a) 出现”undefined” -true 转换成数字是-1 +undefined 显示“NaN”
-1 + NaN = NaN
-1 + NaN + “” = “NaN”
“undefined” && “NaN”转换成 boolean,就都是 true
“11”* 2 是*把两边转换成了数字,所以 11 +“11”* 2 =33,33 == 33,两边相等
!!非非就是正
“ ”这不是空串,是空格字符串
!!” ”转换成 Boolean 为 true
!!””非非空串,转换为 Boolean 为 false
!!false 就是 false
true + false - false = 1 + 0 – 0 =1
11|| document.write(‘你觉得能打印?’)
||遇到真就听,1 为真,所以返回 1
例(window.foo || (window.foo = ‘bar’));求 window.foo
答案”bar”
这道题要先看(window.foo = ‘bar’)这一边的,再看左边的 window.foo,因为运算
符的顺序;但是这道题错误的读法(从左到右)也是 bar
(window.foo || window.foo = ‘bar’);这么写就报错;||或运算符优先级高于=等号
8.作用域精解
[[scope]]:每个 javascript 函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供 javascript 引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
运行期上下文:当函数在执行的前一刻,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。
查找变量:在哪个函数里面查找变量,就从哪个函数作用域链的顶端依次向下查找。函数类对象,我们能访问 test.nametest.[[scope]]隐式属性——作用域
例 function test (){
}
第一次执行 test(); → AO{} //AO 是用完就不要的
第二次执行 test(); → AO{} //这是另外的 AO
例 function a (){
function b (){
var bb = 234;
aa = 0;
}
var aa = 123;
b();
console.log(aa)
}
var glob = 100;
a();
0 是最顶端,1 是次顶端,查找顺序是从最顶端往下查
function a () {
function b(){
var bb=234;
aa=0;
}
var aa=123;
b();
console.log(aa)
}
var glob=100;
a();
答案:0
理解过程:bb 的 AO 是拿到 aa 的 AO,就是同一个 AO,bb 只是引用了 aa 的 AO,
GO 也都是同一个。function b(){}执行完,干掉的是 b 自己的 AO(销毁执行期上下
文)(去掉连接线),下次 function b 被执行时,产生的是新的 b 的 AO。b 执行完只
会销毁自己的 AO,不会销毁 a 的 AO。function a(){}执行完,会把 a 自己的 AO 销毁
【会把 function b 也销毁】,只剩 GO(回归到 a 被定义的时候),等下次 function a
再次被执行时,会产生一个全新的 AO,里面有一个新的 b 函数。。。。。。周而复始
理解过程
a 被定义 a.[[scope]] → 0 : GO{}
a 被执行 a.[[scope]] → 0 : aAO{}
1 : GO{}
b 被定义 b.[[scope]] → 0 : aAO{}
1 : GO{}
b 被执行 b.[[scope]] → 0 : bAO{}
1 : aAO{}
2 : GO{}
c 被定义 c.[[scope]] → 0 : bAO{}
1 : aAO{}
2 : GO{}
c 被执行 c.[[scope]] → 0 : cAO{}
1 : bAO{}
2 : aAO{}
3 : GO{}
当 c 执行完后,会干掉自己的 cAO,回到 c 被定义的状态,当 c 再被执行时,会生
成一个新的 newcAO{},其余都一样,因为基础都是 c 的被定义状态
c 被执行 c.[[scope]] → 0 : newcAO{}
1 : bAO{}
2 : aAO{}
3 : GO{}
如果 function a 不被执行,下面的 function b 和 function c 都是看不到的(也不会被执
行,被折叠)。只有 function a 被执行,才能执行 function a 里面的内容
a();不执行,根本看不到 function a (){}里面的内容
11.闭包
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。
内存泄漏就是内存占用,内存被占用的越多,内存就变得越来越少了,就像内存被
泄露了一样
例
fucntion a (){
function b (){
var bbb=234;
console.log(aaa)
}
var aaa=123;
return b;
}
var glob=100;
var demo = a();
demo();
return b 以后,就返回出去,再销毁 fn a
答案 123。因为没有 b();此时 b 还是被定义的状态,和 a 执行的状态是一样的。function a(){}是在 return b 之后才执行完,才销毁。return b 让 a 执行时的 AO 被保存在外面。
return b 是把 b(包括 a 的 AO)保存到外部了(放在全局)
当 a 执行完砍掉自己的 AO 时,b 依然可以访问到 a 的 AO(因为 return b)
但凡是内部的函数被保存到外部,一定生成闭包
例子
function a(){
var num =100;
function b (){
num ++;
console.log(num);
}
return b ;
}
var demo = a();
demo()
demo()
答案 101,102 理解过程
a 被执行 0 : a AO: num = 100;
1 : GO: demo = a();
b 被执行 0 : bAO :
1 : aAO : num = 100;
2 : GO: demo = a();
在第一次执行 function b 时,num ++就把 aAO 变成{num : 101},当 function b 执行完毕时,剪断的是 bAO,而 aAO 不变,当执行 function a 的 return b 时就把 aAO,GO 都存在了外部,执行完 a 销毁 scope 时去掉 a 的连接线,但是因为 return b 把 aAO,GO存在了外部,所以依然还是可以访问值
在第二次执行 function b 时,aAO{num : 101},在 num ++就是 102
function a(){
var aa=345;
function b(){
var bb=234;
function c(){
var cc=123;
}
c()
}
b()
}
a();
执行过程:
先执行 function a(){ var aa = 345;function b (){}b();}
想要执行完上面的 b();就需要执行完 function b(){var bb = 234; function c(){}c();},
想要执行 c();就要先执行完 function c(){var cc =123;},
b();是一个复合语句,执行完 b 里面的每一句话,才能执行 b();
销毁顺序:
哪个先被执行完,哪个先被销毁
当 c 执行完,先销毁 c 自己的执行期的上下文,
当 c();执行完,那么 b 也执行完了,就销毁 b 的执行期上下文;
当 b();执行完,那么 a 也执行完了,就销毁 a 的执行期上下文
1.闭包的作用
1、实现公有变量
例子:函数累加器
每回调用 counter 就会在原有基础上加一次
2.可以做缓存(存储结构)
eg:eater。缓存是外部不可见的,但是确实有存储结构
答案 101 和 100,思考过程:说明两个用的是一个 AO
test doing test[[scope]] 0:testAO
1:GO
a defined a.[[scope]] 0 : testAO
1 : GO
b defined b.[[scope]] 0 : testAO
1 : GO
return[a, b]将 a 和 b 同时被定义的状态被保存出来了
当执行 myArr[0]();时
a doing a.[[scope]] 0 : aAO
1 : testAO
2 : GO
当执行 myArr[1]();时
b doing b.[[scope]] 0 : bAO
1 : a 运行后的 testAO
2 : GO
a 运行后的 testAO, 与 a doing 里面的 testAO 一模一样
a 和 b 连线的都是 test 环境,对应的一个闭包
function a 和 function b 是并列的,不过因为 function a 在前,所以先执行 num ++,在
执行 num --
myArr[0]是数组第一位的意思,即 a,myArr[0]();就是执行函数 a 的意思;
myArr[1]是数组第二位的意思,即 b,myArr[1](); 就是执行函数 b 的意思
例缓存的应用,对象里面可以用属性和方法
答案 i am eating banana,eat 和 push 操作的是同一个 food
在 function eater(){里面的 food}就相当于一个隐式存储的机构
obj 对象里面是可以有 function 方法的,也可以有属性,方法就是函数的表现形式
3.可以实现封装,属性私有化
eg: Person();
4.模块化开发,防止污染全局变
12.立即执行函数
1.定义:
此类函数没有声明,在一次执行过后即释放(被销毁)。适合做初始化工作。
针对初始化功能的函数:只想让它执行一次的函数
立即执行的函数也有参数,也有返回值,有预编译
例(function (){ //写成(function abc(){}())也调用不到
var a = 123;
var b = 234;
console.log(a + b);
}())
例(function (a, b, c){
console.log(a + b + c * 2);
}(1, 2, 3))这一行里面的(1,2,3)是实参
例 var num = (function (a, b, c){
var d = a + b + c * 2 – 2;
return d;
}(1, 2, 3))
答案 num = 7
2.立即执行函数的两种写法
- (function (){}()); //在 W3C 建议使用这一种
- (function (){})();
1.只有表达式才能被执行
2.能被执行符号执行的表达式,这个函数的名字就会被自动忽略(放弃名字)
3.能被执行符号执行的表达式基本上就是立即执行函数
4.函数声明和函数表达式是两个东西,虽然都能定义函数
函数声明和函数表达式是两个东西,虽然都能定义函数
函数声明:function test ( ){}
函数表达式:var test = function( ){}
例 function (){
var a = 123;
}()
答案这是函数声明,不能执行,报语法错误,因为只有表达式才能被执行符号执行
例 function test(){
console.log(‘a’);
}
答案这也是函数声明
例 function (){
var a = 123;
}
test();
答案 test();就是表达式,所以能执行
例 var test = function (){
console.log(‘a’);
}()
答案这是表达式,可以被执行,此时在控制台执行 test 的结果是 undefined,这个函
数的名字就会被放弃
例+ function test(){
console.log(‘a’);
}()
答案加了个“正“,在趋势上要把他转换成数字,就是表达式了,既然是表达式就
能被执行,就会放弃名字,此时 console.log (test),就会报错;这就是立即执行函数
同样放了正号,负号,!就会放弃函数名字,转换成表达式;但是*和/不行,&&||前
面放东西也行
例 var test = function (){}
其中= function (){}把 function 赋到 test 里面去叫表达式,var test 是声明
在执行时,会放弃这个函数,储存到 test 里面储存引用,让这个 test 恢复到被声明的状态
例(function test(){console.log(‘a’);})()
这个被()包起来的 function 函数声明变表达式了,就能被外面的最后的()执行
例(function test(){console.log(‘a’);} ())
最外面的大括号是数学运算符号,是最先被执行,其余的括号都是有语法意义的,
就把函数变表达式了
()也是数学执行符,能打印 a,但是执行 test 就报错,所以干脆就不写 test
例 function test (a, b, c, d){
console.log(a + b + c + d);
}(1, 2, 3, 4); //写成(1)也是这种效果
理论上不能执行,只写()就会被当成执行符,但是(1, 2, 3, 4);这样写暂时不会当成运
算符,没意义,但是不会执行,也不报错。还能调用 test
例先定义一个 10 位数的数组,就是在 var arr = [function () {console.log(i);}有十个]并
且把数组返回
function test (){ //定义个函数 test
var arr = []; //定义一个空数组
for (var i = 0; i < 10; i ++){
//丰满空数组,让空数组添加十条数组,
每一条都是一个 function(){}
arr[i] = function () { //随着 for 循环 i 变,
数组 i 也变,arr 每一次都等于一个全新的函数体
document.write(i + “ ”);
}
}
return arr; //把 arr 返回到外部
}
var myArr = test();
for (var j = 0; j < 10; j ++){ //分别执行十个函数体,函数体里面定义了 document.write
myArr[j]();
}
答案 10 10 10 10 10 10 10 10 10 10
第二个 for 是为了打印这个数组,麻烦写法 myArr[0](); myArr[1](); 。。。myArr[9]();
过程 for (var i = 0; i < 10; i ++){}执行了十次,产生了十个彼此独立的函数。并且把这
十个函数放在数组里面去,还把数组返回了,这十个函数和 test 一起产生了一个闭
包。
既然是闭包,那么访问 test 里面的变量时,实际上访问的是同一套,而 test 产生了
arr 和 i 变量(写在 for 循环里面的 i 变量),而这十个函数在外边要访问 i 变量,其实
访问的是同一个 i。
什么时候访问的?在 test 执行完以后,在下面 for(j)访问的
第一个 i=0,转到 9 的时候,i ++变 10 终止 for 循环,结束的时候 i=10,结束之后把
return arr 返回,arr;
这十个函数都是为了打印 i 的,在外部访问 i 的时候 i=10,所以打印的是 10
arr[i] = function () {
document.write( i + “ ”);
}
理解过程:在这个函数体中,当 arr[0] 时,document.write(i)的 i 是不变的,还是 i,
等函数保存到外部之后,等执行的时候,才会去找 i 的值。
这个赋值语句中,arr[0] = 函数;把一个函数体或者说是一个函数引用赋给数组的当前
位,数组的当前位需要马上被索取出来的(数组现在是当前第几位,我们是知道的,
因为这个是执行语句),当 for(var i = 0)时,arr[i]会变成 arr[0],但是这个 i 跟函数体
里面的 d.w(i+“”)里面的 i 是没有关系的,因为函数体 function(){}不是现在执行,
不会在意函数里面写的是什么,不是现在执行那么里面的 document.write 不会变成现
实的值,不是现在执行就是函数引用(函数引用就是被折叠起来的,系统不知道里
面写的是什么)
在执行 myArr[j]();的时候,系统才会读 document.write(i +””)里面的语句
在定义函数的时候是不看里面的,在执行的时候才看
例我们让上面这个变成打印 0,1,2,3,4,5,6,7,8,9,用立即执行函数解决
function test (){
var arr = [];
for (var i = 0; i < 10; i ++){
(function (j) {
arr[j] = function () {
document.write( j + “ ”);
}
}(i));
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j ++){
myArr[j]();
}
理解过程:相当于在 for 循环里面有十个立即执行函数 function(j){}
在第一圈 i 是 0,j 也是 0,function(){document.write( j + “ ”)}拿着 j=0,进行循环
的第二圈 i 是 1,又有了一个新的 j 是 1,反复循环
形成了十个立即执行函数,有十个 j 对应
例 for(var i = 0; i < 10; i ++){
console.log(i);
}
答案 0,1,2,3,4,5,6,7,8,9
例 for(var i = 0; i < 10; i ++){
(function(){
}())
}
中间 function 这个会执行 10 次
13.对象
1.对象的创建方法
- var obj = {} 对象字面量/对象直接量 plainObject
- 构造函数
- 系统自带的构造函数 Object()
new Object();Array();Number();Boolean();Date();
系统自带的构造函数 Object()可以批量生成对象,每一个对象都一样,但是彼此相互独立。在 Object()前面加个 new,变成 new Object()的执行,就会真正的返回一个对象,通过 return 返回,拿变量接受。var obj = new Object();
var obj = new Object();和 var obj = {};这样写区别不大
- 系统自带的构造函数 Object()
例 var obj = new Object();
obj.name = ‘abc’;
obj.sex = “male”;
双引号和单引号都是表示的字符串,写双引号也可以写单引号,但是为了跟后端 php
配合最好写单引号。如果要打印一个单个的引号,用正则表达式转义字符
注意:等号和冒号的用法
obj.say = function(){}
var obj = { name : ‘abc’}
- 自定义
Object.create(原型)方法
例 function Person(){}
Person 是可以随便写的,也是构造函数
构造函数跟函数结构上没有任何区别
例 var person1 = new person();
必须用 new 这个操作符,才能构造出对象
构造函数必须要按照大驼峰式命名规则,但凡是构造函数就要大写,例如 TheFirNa
2.构造函数内部原理
前提必须要加 new,以下三步都是隐式的:
- 在函数体最前面隐式的加上 var this = {} 空对象
- 执行 this.xxx = xxx;
- 隐式的返回 return this
例 function Person(name, height){
//隐式的 var this = {},下面正常执行 this
this.name = name;
this.height = height;
this.say = funtion (){
console.log(this.say);
//此处的 this 和外面的 this 不同
}
// 隐式的 return this;
}
//例也可以这样显式的写出来
function Person (name, height){
var that = {}; //显式写出来
that.name = name;
that.height = height;
return that; //显式写出来
14.包装类
new String(); new Boolean(); new Number()
var num =123; → 原始值数字
只有原始值数字是原始值,原始值不能有属性和方法
属性和方法只有对象有,包括对象自己,数组,function
字符串类型的对象
var num = new Nunber(123); //数字类型对象
var str = new String(‘abcd’); //字符串类型对象
var bol = new Boolean(‘true’); //布尔类型对象
不能写成 undefined.abc = 123;会报错
undefined 和 null 不可以有属性
例
原始值不可能有属性和方法,但经过了包装类(加隐式)可以调用一些属性与方法
var num = 4;
num.len = 3;
//系统隐式的加上 new Number(4).len = 3; 然后 delete
console.log(num.len);
//系统隐式的加上 new Number(4).len; 但是这个 new number 和上面的 new number
不是同一个,所以返回 undefined
而上面这些隐式的过程就是包装类
例子
例 var str = “abcd”;
str.length = 2;
//隐式的加上 new string(‘abcd’).length = 2; delete
console.log(str);
console.log(str.length);
答案是 abcd,4
例子
var str = "abc";
str +=1
var test = typeof(str)
if(test.length==6){
test.sign='typeof的返回结果可能为String'
}
console.log(test.sign)//undefined
解析
str += 1; //abc1
var test = typeof(str); //test == “string”,返回 string,string 长度是 6
if(test.length == 6){
test.sign = “typeof 的返回结果可能为 String”; //这是原始值,原始值要赋属性
值需要调用包装类,赋了跟没赋值是一样的,new String(test).sign=’xxx’;
}
conlogo.log(test.sign); //new String(test).sign
答案 undefined
例:请问以下表达式的结果是什么?
parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
A 3,3,3
B 3,3,NAN
C 3 NAN,NAN
D other
答案选 3 或 4,值为 3,NaN,3(有的浏览器遇到 0 是报 NaN)
例:以下哪些是 JavaScript 语言 typeof 可能返回的结果:
A.string B.array C.object D.null
答案:A
例:看看下面 alert 的结果是什么?
function b (x, y, a) {
arguments[ 2 ] = 10;
alert( a );
}
b(1, 2, 3);
如果函数体改成下面,结果又会是什么?
a = 10;
alert(arguments[ 2 ]);
答案 10,10
15.原型
1.定义
- 原型是 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
- 利用原型特点和概念,可以提取共有属性。
- 对象属性的增删和原型上属性增删改查。
- 对象如何查看原型 ==> 隐式属性 proto。
- 对象如何查看对象的构造函数 ==> constructor。
例
person.prototype //原型(描述一种继承关系),出生时就被定义好了
person.prototype ={} //是祖先
右上的 person 和 person1 都有一个共有的祖先 Person.prototype
例
自己身上有属性,原型上也有属性,取近的,用自己的
例
将左上的共有的东西提取出来放在原型里面,如右上图
上面通过对象(后代)改原型(祖先)是不行的,在对象里面修改,只作用给自己
改原型都不行,增加肯定也不行。对象可以删除属性
左上的简化写法见右上 // construnctor 是构造的意思(隐式是浅粉色)
浅粉色是系统帮你设置的,深紫色是自己设置的
在原型内部自带 constructor,指的是 Car。通过 constructor 能找的谁构造的自己
constructor 可以被人工手动更改
1.浅粉色的__proto__是系统的隐式的属性,前面两个_后面两个_,可以修改,尽量不改。
2.在开发的时候,如果很私人可以写成_private,告诉同事别动。
3.上面的__proto__放的是原型。__proto__存的对象的原型
4.上面的 var this ={proto:person.prototype};这个对象并不是空的,这个 proto,当你访问这个对象的属性时,如果对象没有这个属性,那么就会访问 proto 索引,看看有
没有。有一个连接的关系,原型和自己连接到一起
Person 的原型是可以被修改的
Person.prototype.name 这种.的写法是在原有的基础上把值改了。改的是属性,也就是
房间里面的东西。
而 Person.prototype={name:’cherry’}是把原型改了,换了新的对象。改了个房间。
上面在 new 的时候 var this = {proto:Person.prototype}里面的指向 Person,此时
Person.prototype 与__proto__指向的是一个空间,把他返回给 var person。
先 new 再 Person.prototype={name:’cherry’}已经晚了
在 Person.prototype={name:’cherry’}时,Person.prototype 空间改了,但是__proto
指向的空间不变。
上面的步骤实际上是
例
上面这种思考过程:程序执行顺序
1.先把 function Person(){}在预编译的过程中提到最上面
2.再执行 Person.prototype.name = ‘sunny’这一样行
3.再执行 Person.prototype = {name:’cherry’}
4.最后执行 var person = new Person();执行到 new 的时候,才会发生//var this ={proto:Person.prototype}
5.下面的把上面的覆盖了
6.答案是 cherry
执行 son.toString //返回 function toString( ){ [native code] },这里返回的是原型链
终端的 toString
Grand.prototype.proto = Object.prototype // Object.prototype 是原型链的终端
2.原型链
1、如何构成原型链?(见上一个例子)
2、原型链上属性的增删改查
原型链上的增删改查和原型基本上是一致的。只有本人有的权限,子孙是没有的。
3、谁调用的方法内部 this 就是谁-原型案例
4、绝大多数对象的最终都会继承自 Object.prototype
5、Object.create(原型);
6、原型方法上的重写
例
son.fortune.card2=’master’这种改,这是引用值自己的修改。属于 fortune.name 给
自己修改,这是一种调用方法的修改
例
例
console.log(this.name); //如果写成 name 就会错,没有这个变量
例
a.sayName ( )方法调用,就是 say.Name 里面的 this 指向,是谁调用的这个方法,this就指向谁
例
this.height ++; //这后面默认有一个 return undefined
例 var obj = { };也是有原型的
var obj = { };与 var obj1 = new Object( );效果是一样的
写 var obj = { }; 系统会在内部来一个 new Object( );
obj1.__proto__ → Object.ptototype;
但是在构造对象时,能用对象自变量 var obj = { };就不要用 var obj1 = new Object( );
例
对像自变量的原型就是 Object.ptototype;
3.Object.create(原型);
var obj = Object.create(原型);
Object.create 也能创建对象。var obj = Object.create(这里必须要有原型)
绝大多数对象的最终都会继承自 Object.prototype
例
html 里面没有添加任何东西
这样就报错
例
html 里面没有添加任何东西
只在控制台加上 null
例
原型是隐式的内部属性,你加是没有用的
例
Object.create()在括号里面只能放 null 或者 Object,其余会报错
例
undefined 和 null 没有原型,也就不可能有 toString 方法
例
下面 123.toString 首先会识别成浮点型,所以在后面直接加.toString 是不行的
数字想用 toString 方法,要经过包装类包装 new Number(num)然后. toString
例
加深上面的理解
而 new Number(num). toString 的原型是 Nunber.prototype,而 Nunber.prototype 上面有
一个.toString 方法,Nunber.prototype 也有原型 Nunber.prototype.proto,原型是
Object.prototype
假如 new Number 上面的 prototype 上面有这个 toString,那么就不用 Object.prototype
的 toString。而这个 number 上面有这个 toString。
然后 number 上面的 toString 调用的是自己重写的 toString。
原型上有这个方法,我自己又写了一个和原型上同一名字,但不同功能的方法,叫
做重写(同一名字的函数,不同重写方式)
通过返回值,形参列表不同传参
同样的名实现不同功能的,就是重写
例
和原型链上终端方法名字一样,但实现不同的功能,叫做方法的重写。也就是覆盖
例子
下面这个也是重写
例
让 object 上面的 toString 重写了。
所以 num.toString()调用的是 number. prototype.toString。
如果调用的是 object. prototype.toString 结果会不一样。
例子
document.write 会隐式的调用 toString 方法,其实打印的是 toString 的结果
没有原型就不能 toString
上面这个例子表示:我要打印的是 obj,实际上打印出来的是 toString 方法,也证明
了 document.write 调用的是 toString 方法
16.call/apply
1.作用
改变 this 指向。
2.区别
后面传的参数形式不同。
例
任何一个方法都可以.call
.call 才是一个方法执行的真实面目
接执行 Person.call ( )和 Person ( )没有区别
Person.call( );括号里面可以传东西
如果 Person.call( obj );里面的 call 让 person 所有的 this 都变成 obj不 new 的话,this 默认指向 window。
call 的使用必须要 new
call 的第一位参数用于改变 this 指向,第二位实参(对应第一个形参)及以后的参数
都当做正常的实参,传到形参里面去
借用别人的方法,实现自己的功能。
例
A写 test()和写 test.call()是一样的,
例
两个人的需求
有重复部分
call 改变 this 指向,借用别人的函数,实现自己的功能。
只能在你的需求完全涵盖别人的时候才能使用
如果不想要 age 这个,就不能使用这种方法
Person.call(this, name, age, sex);里面的 this 现在是 new 了以后的 var this={}
利用 Person 方法,实现了 Student 自己的封装
例
apply 也是改变 this 指向的,只是传参列表不同,第一位也是改变 this 指向的人,第
二位,apply 只能传一个实参,而且必须传数组 argunments
call 需要把实参按照形参的个数传进去
new 以后才有意义
3.继承发展史
1.传统形式 ==> 原型链
问题:过多的继承了没用的属性
2.借用构造函数 =>利用 call、apply 所以不算标准的继承模式
- 不能继承借用构造函数的原型
- 每次构造函数都要多走一个函数 =>浪费效率
this 放进去的前提,这个函数必须是 new 来的
3.共享原型(较好的继承方法)
不能随便改动自己的原型
Son.prototype=Father.prototype
让 son 和 father 共享原型
例
可以用上面的方式封装函数,实现一个继承
extend 和 inherit 都是继承的意思。
inherit 是 css 的一个值,也是继承的意思。
文字类属性都有一个传递的特性:子元素没有设置文字类属性,子元素默认继承父元素的属性。
font-size:inheit;我没有就继承父亲的
在 inherit(Target,Origin)里面传进去的值是构造函数,需要大驼峰式书写,origin
是原始的意思,让 target(目标)继承 origin
上面这种方式让 son 里面有了 father 原型的属性
应该是先 inherit 继承,后使用
下面这种方式就是先继承了,后改变原型已经晚了,因为他继承的还是原来的空间
例
下面这种写法,son.prototype 和 father.prototype 指向的是一个房间,改 son 就改了
father。我们希望 son 用的 father 的原型,但是改变 son 自己的属性不影响 father
4.圣杯模式
圣杯模式是在方法三的共有原型,但是在共有原型的基础上有改变。
共享原型是:son.prototype=father.prototype
圣杯模式是:另外加个构造函数 function F(){}当做中间层,然后让 F 和 father 共
有一个原型 F.prototype=father.prototype,然后 son.prototype = new F();使用原
型链形成了继承关系,现在改 son.prototype 就不会影响 father.prototype
例
原型上默认有个 constructor
constructor 默认指向他的构造函数
son. constructor 应该指向 Son
指向 father 就是混乱了
所以要指一下
我们希望我们构造出的对象,
能找到自己的超类,超级父级
(究竟继承自谁)应该起名为
super 但这个是保留字,我们
就以 uber
例:
左下这种方法就不好使了,相当于右下。还是原型指向有问题,new 的时候用
的是原来的原型,再 F.prototype =father.prototype 没用,son.prototype 没发生改变
例,
在雅虎时代,封装了 YUI3 库来解决方法三的不足,与圣杯模式相似。现在不用
YUI3 库,现在用 jq
上面的 var inherit 与右边
的 var inherit 是一样的意思
建议写上面的这
4.联系到闭包作用
可以实现封装,属性私有化。
例
为什么在外部执行的 divorce 能用内部的变量?能换成 xiaozhang?
因为 this.divorce 在对象上,由于对象被返回了,这个方法也被返回了。
因为闭包。这个函数被储存到了外部,所以储存了这个函数的执行期上下文。所以
可以用这个闭包。所以 var prepareWife 被下面的 this=fn 三个函数共用,这三个函数
分别与 fn Deng 形成了闭包,共同用 Deng 的 AO,所以可以在外部随意存取。
deng.prepareWife 是 undefined 的,表面上看起来不是自己的,但是实际上只有对象
自己通过对象自己设置的方法可以去操作他。外部用户通过对象. prepareWife 是看不
到的。只有自己能看到,就是闭包的私有化的运用。
最后执行完是这个样的
上面的 var F 这个 F 形成了闭包,成为了这个函数的私有化变量,而且变成私有化变
量就更好
17.操作数字的方法
1.toFixed 是保留两位有效数字
例
有个 bug,在控制台 0.14*100
出现 14.0000000000002,是 js 开发精度不准
2. 向上取整
例
向上取整 Math.ceil(123.234)
答案 124
3. 向下取整
例
向下取整 Math.floor(123.999)
答案 123
4. 随机数
例
Math.random()是产生一个 0 到 1 区间的开区间 随机数
例
所以一般在这种情况下,我们不用 toFixed,因为精度不准确
例
用这种方法取整更好,就不会精度不准确
4.极限
之前 js 在小数点后面最多能容纳 15-17 位,但是升级后能用科学计数法表示 如 2e-17(是 2 乘以 10 的负 17 次方)
而小数点前面只能容纳 16 位的运算, 可正常计算的范围是小数点前后 16 位
18.数组
1.数组的常用方法
改变原数组
-
arr.push(); 向数组的末尾追加元素
-
会修改原数组
-
返回值:修改后的length值
-
可以追加多个
arr[1,2,3]; arr1[4,5,6]; arr2[...arr,...arr1]; // ...扩展运算符,将数组展开
-
-
arr.unshift(); 向数组的开头添加元素
- 会修改原数组
- 返回值:修改后的length值
- 可以追加多个
-
arr.pop(); 删除末尾的元素
- 会修改原数组
- 返回值:被删除的数据
- 只能删除一个
-
arr.shift(); 开头删除一个元素
- 会修改原数组
- 返回值:被删除的数据
- 只能删除一个
-
arr.splice(位置,删除元素的个数,要追加的元素); 任意位置添加删除
- 添加或者删除包含对应下标
- 添加数据时:删除元素的个数写0;
- 可以同时删除和添加(先删除,后添加)
- 修改原数组
-
arr.join(“分隔符”) 使用分隔符将数组数据隔开变为字符串
- 不修改原数组
- 返回值是分隔好的字符串
-
arr.slice(截取的起始下标,结束下标) 数组的截取
- 截取时,包含起始下标,不包含结束下标
- 不修改原数组
- 返回值是截取到的数组
- 参数可以是负数,负数表示倒数,只传一个参数表示从起始下标截取到最后
- arr.reverse() 数组倒序
-
返回修改后的数组
-
改变原数组
var arr=[1,5,8,6,9,4,2,3]; console.log(arr.reverse()); // (8) [3, 2, 4, 9, 6, 8, 5, 1] console.log(arr); // (8) [3, 2, 4, 9, 6, 8, 5, 1]
-
不改变原数组
arr.concat 多个数组的连接
-
不修改原数组
-
返回值是连接之后的新数组
var arr1=[1,2,3]; var arr2=[4,5,6]; console.log(arr1.concat(arr2,[7,8,9])); // 控制台输出 // (9) [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.sort() 数组的排序
-
如果没有参数,则从字符的编码开始按顺序排
-
如果有参数,这个参数必须是一个函数(回调函数)这个回调函数有两个参数,分别是a,b
-
修改原数组
-
返回值是排序之后的数组
var arr3=[1,5,6,99,52,663,22,66,552,6,622]; arr3.sort(function(a,b){ //a-b 正序, //b-a 倒序, return a-b; }) console.log(arr3); // 控制台输出 // (11) [1, 5, 6, 6, 22, 52, 66, 99, 552, 622, 663] //箭头函数 arr.sort((a,b)=>a-b) //正序, arr.sort((a,b)=>b-a) //倒序, //箭头函数 arr3.sort((a,b)=>{return a-b}); arr3.sort((a,b)=>a-b);
2.类数组
一旦给一个对象加上splice方法,至此对象就会长的和数组一样变为类数组
push内部原理:
题:
答案:
obj {
"2" : "c"
"3" : "d"
"length" : 4
}
3.数组去重(哈希法)
19.This
20.try...catch
在try的里面发生错误,不会执行错误后的try里面的代码,但是会执行catch里面的代码
错误信息 错误名称(catch只能打印一个错误)
21.es5.0严格模式:
(要写在整个页面代码的最顶端)
也可以写在局部:
22.with(){}
with(){}可以改变作用域链,可以让里面代码最顶端的AO变为()里面的AO(缺点:会使代码运行速度大大降低)
打印obj
23.eval()方法:可以将字符串当做代码执行
打印123