js知识点总结

 

1.变量的定义:

我们向系统申请了一个地方,这个地方门牌号是我们定义好的属性名,然后把属性值赋予属性名。如果重复给同一属性名赋值,最后赋值的一个将成为最终的属性值。

命名规则:

a. 变量名必须以英文字母,_,或$开头

b. 变量名可以为英文字母、_、$或者数字

c. 不可以用系统的关键字和保留字作为变量名

2.值类型-数据类型

不可改变原始值:(有五种,存放在stack / 栈中。first in,last out) (不可改变的含义是每次赋值都会重新开辟一个stack)

Number String Boolean undefined null(null用来占位置的)

引用值:(引用值的值存放在堆里面,栈里面存放的是该引用值堆的地址)

object array function 

3.js常见的错误

①低级错误

var a = 1:

如果英文写成了中文或者多写括号少些括号等低级错误,机器会在执行一行一行代码之前通篇扫描中检查出来,一行代码都不会被执行。

②逻辑错误


   
     
     
     
     
  1. let a = 1;
  2. document. write(a);
  3. document. write(b);
  4. document. write( 20);

第一次通篇扫描扫不出来,在执行一行解释一行时,会发现b未定义,报错,20也不会被输出。

③一个代码块的错误不会影戏其他代码块。

4.js运算符

①运算操作符

a. “+”

第一个作用是两个Number的数字相加

第二个作用是任何东西加字符串都等于字符串

b. - * / % = ()


   
     
     
     
     
  1. // 特殊的除法
  2. 0/ 0
  3. // 结果为NaN
  4. 1/ 0
  5. // 结果为Infinity
  6. 0/ 1
  7. // 结果为0
  8. - * / % = ()的优先级
  9. ()的优先级最高 = 的优先级最低

备注:Infinity不可能传给后端,Infinity要先变成字符串,再传给后端,后端进行解析

c. ++ -- += -= *=  /= %=


   
     
     
     
     
  1. let c = c + 1; 等价于 let c += 1;
  2. let a = 10;
  3. let b = ++a - 1 + a++;
  4. document. write(b + "" + a);
  5. // 执行结果:b = 11 -1 + 11; a = 10 + 1 + 1 = 12
  6. // ++a 是a先加 a++ 是a后加

   
     
     
     
     
  1. // 小栗子
  2. 如果让 let a = 123, b = 456;中的a和b互换值
  3. a = a + b;
  4. b = a - b;
  5. a = a - b;

②比较运算符

> < == >= <= != 


   
     
     
     
     
  1. // 小栗子
  2. var a = 'a' > 'b';
  3. document. write(a) // 输出为false
  4. reason: 字符串比较的是 Ascll码,其中a的的ascll码 小于b的ascll 'a' = "A" + 32 = 113 ; 'A' = 81
  5. //根据上面的结论再来一个 小栗子
  6. var a = '10' > '8'
  7. document. write(a) // 输出为false
  8. reason:先是 '1''8'比较,小于,直接返回 false, 否则一直比下去返回值
  9. // 特殊规则
  10. a = 1 == 1; 输出为 true
  11. a = undefined == undefined 输出为 true
  12. a = undefined == null 输出为 true
  13. a = Infinity == Infinity 输出为 true
  14. a = NaN == NaN 输出为 false == > NaN不等于任何东西,一急眼自己都不认识

③逻辑运算符(重点)


   
     
     
     
     
  1. && || !
  2. && ===> 会先看&&前面的表达式转换为 Boolean,如果只有两个表达式的话,第一个为 false,那么直接返回第一个值的结果,否则直接返回第二个值的结果。
  3. // 碰到假就停下返回
  4. // 小栗子 (&& 可以当做短路来判断逻辑)
  5. var a = 1 && 2 + 2; 输出的结果为 4
  6. var a = 0 && 2 + 2;输出的结果为 0
  7. 2 > 1 && document. write( '你真棒') 输出的结果是 '你真棒';
  8. || ===> 如果只有两个表达式的话,如果第一个值是 true, 那么直接返回第一个值的结果,否则返回第二个值的结果
  9. // || 碰到真就返回
  10. ! ===> 取反 把这个东西转换为 Boolean取反再取反
  11. // 小栗子
  12. var data = {};
  13. var a = !! data;
  14. document. write(a); 输出为 true

5.条件语句

if ,if  else


   
     
     
     
     
  1. // if 和 && 的转换小栗子
  2. if( 1 > 2) {
  3. document. write( 'a');
  4. }
  5. 等价于
  6. 1 > 2 && document. write( 'a');

  switch case 


   
     
     
     
     
  1. // 根据浏览器端输入的值的不同执行不同的操作
  2. let n = parseInt( window. prompt( 'input'));
  3. switch(n) {
  4. case 'a':
  5. console. log( 'a');
  6. break;
  7. case 'b':
  8. console. log( 'b');
  9. break;
  10. case 'c'
  11. console. log( 'c');
  12. }

条件语句中的continue 和 break


   
     
     
     
     
  1. // continue
  2. for( var a = 0; a < 100; a++) {
  3. if(a % 7 === 0 || a % 10 === 7) {
  4. continue;
  5. }
  6. console. log(a);
  7. }
  8. // for循环中如果a逢7或是7的倍数那么跳出本次循环,输出其他的小于100的数字
  9. // break
  10. // 判断今天输入的date是星期几
  11. let date = parseInt( window. prompt( "input"));
  12. switch(date) {
  13. case "monday":
  14. case "tuesday":
  15. case "wednesday":
  16. case "thursday":
  17. case "friday":
  18. console. log( "工作日");
  19. break;
  20. case "monday":
  21. case "sunday":
  22. console. log( "休息日");
  23. }
  24. // 上述程序如果没有break,会一直走完case "sunday"

 

6.循环语句

①for循环


   
     
     
     
     
  1. // 如何方便地打印10个a
  2. for( var a = 0; a < 10; a++) {
  3. document. write(a)
  4. }
  5. // 原理分析
  6. 第一步 var a = 0;
  7. 第二步 if(a < 10) { document. write(a) }
  8. 第三步 a++;
  9. 第四步 if(a < 10) { document. write(a) }
  10. 第五步 a++;
  11. 第六步 if(a < 10) { document. write(a) }
  12. ... 直到 a = 10 退出循环 输出 1 2 3 4 5 6 7 8 9
  13. // 以上的for循环还可以写成下面的
  14. var a = 0;
  15. for(;a < 10;) {
  16. document. write(a);
  17. a++;
  18. }
  19. // 小测试
  20. // 在for循环的()中只能写一句话,执行体只能写一句话,打印100个数
  21. var a = 100;
  22. for(; a-- ;) {
  23. document. write(a + '');
  24. }
  25. 输出 99 ~ 0

②while循环


   
     
     
     
     
  1. // 如果for()里面只写中间部分,比如for(; a < 10 ;)和while循环是完全一样的
  2. var a = 0;
  3. for(;a < 10;) {
  4. document. write(a);
  5. a++;
  6. }
  7. 等于
  8. var a = 0;
  9. while(a < 10) {
  10. document. write(a);
  11. a++;
  12. }
  13. // 小栗子
  14. // 写一个逢7 或 7的倍数输出打印切小于100
  15. var a = 100;
  16. while(a % 10 === 7 || a % 7 === 0) {
  17. document. write(a);
  18. a ++;
  19. }

③do{} while() 循环

// do{} while() 循环,不管满足不满足条件,都会执行一次

   
     
     
     
     

7.现在穿插几个小逻辑题

①求2^n, n从1开始, n用户输入


   
     
     
     
     
  1. var n = parseInt( window. prompt( 'input'));
  2. var mul = 1; // 用来存储2的n次方
  3. for( var i = 0; i < n; i++) {
  4. mul *= 2;
  5. }
  6. console. log(mul)
  7. // 在浏览器中输入10 输出 1024

②求n!,  n为用户输入


   
     
     
     
     
  1. var n = parseInt( window. prompt( "input"));
  2. var mul = 1; // 用来存储n!
  3. // 因为1!是1,所以初始化i为1
  4. for( var i = 1; i <= n; i++) {
  5. mul *= i;
  6. }
  7. console. log(mul);
  8. // 输入10 输出 3628800

③输出100以内的质数(质数是只能被1或者自身整除)


   
     
     
     
     
  1. for( var i = 2; i < 100; i ++) {
  2. var count = 0
  3. for( var j = 1; j < Math. sqrt( 100); j++) {
  4. if(i % j === 0) {
  5. count ++;
  6. }
  7. }
  8. if(count === 2) {
  9. console. log(i)
  10. }
  11. count = 0;
  12. }

8.初始化引用值

①数组


   
     
     
     
     
  1. // 数组里面可以存放很多东西,如 1, 'a', undefined, true,甚至可以是数组
  2. // 小栗子 打印数组中的每一项
  3. var arr = [ 1, 2, 3, undefined, 'abc'];
  4. let len = arr. length;
  5. for( let i = 0; i < len; i++) {
  6. console. log(arr[i])
  7. }
  8. 输出 1 2 3 undefined abc

②对象


   
     
     
     
     
  1. // 以前编程面向过程,后来出现了面向对象(按照人的思维方式来思考操作),对象也是存储数据的仓库,要比数组更加直观一点,对象中的每个属性值都要有属性名,属性名可以加双引号,可以不加,属性值可以放boolean/undefined/null等,还可以存放数组/对象
  2. //小栗子 定义一个对象
  3. var lei = {
  4. lastName: 'lei',
  5. age: '23',
  6. sex: 'man',
  7. wife: 'baicai'
  8. }
  9. // 如何取对象的值
  10. lei. lastName ==> 输出 lei
  11. // 如何赋值
  12. lei. firstName = 'zhou';
  13. // 如何删除对象的属性
  14. delete lei. age 删除成功返回 true

9.编程形式的区别


   
     
     
     
     
  1. 1.面向过程
  2. c就是面向过程,它会有机械的想法,第一步想要干嘛,第二步想要干嘛
  3. // 比如 人想要飞
  4. 机械: 给人焊一个翅膀,从高处跳下就能飞
  5. 人: 不可能
  6. 2. 面向对象
  7. 人的编程思想
  8. // java javascript c++ 面向对象语言,后来javascript 面向对象和面向过程

10.typeof 操作符(返回的都是 'xxx')

①六中数据类型

number string boolean undefined object(null array object ) function 


   
     
     
     
     
  1. // typeof 小栗子
  2. var num = 123; typeof(num) // 输出 "number"
  3. var num = [] | {} | null; typeof(num) // 输出 "object"
  4. var num = undefined; typeof(num) // 输出 "undefined"
  5. var num = function( ) {}; typeof(num) // 输出 "function"

②用法

a. typeof(xxx)  使用typeof(数据)

b. typeof xxx 中间加空格

不过最好使用a方案


   
     
     
     
     
  1. // 笔试题
  2. typeof( typeof undefined)
  3. // 输出 "string"

11.类型转化之显式类型转换

①Number() 

用途: 看起来不像数字的转换得到NaN,Number目的是千方百计地把()中的值转换为数字


   
     
     
     
     
  1. // 小栗子
  2. var num = Number( '123'); 输出 123
  3. 下面是试了一下其他值的转换
  4. true ==> 1
  5. undefined ==> NaN
  6. null ===> 0
  7. 'a' ===> NaN
  8. '2' * '1' ===> 2
  9. '2' - '1' ===> 1
  10. '123abc' ===> NaN

②parseInt()

用途1: 把()中的值转换为整型,小数点的部分去掉,parseInt()的目的是把数字和字符串的数字(数字后面加字符串)转换为整型,

parseInt从数字位一直截到非数字位


   
     
     
     
     
  1. // 小栗子
  2. var a = 123.99;
  3. parseInt(a);
  4. // 输出 123
  5. a = '123abc';
  6. parseInt(a);
  7. // 输出 123
  8. a = 'abc123';
  9. parseInt(a);
  10. // 输出 NaN

用途2: parseInt(number, radix(基底)), 以radix为基底进制的数字转换为10进制的数字


   
     
     
     
     
  1. // 小栗子
  2. var demo= '10';
  3. var num = parseInt(demo, 16);
  4. console. log( typeof(num) + ' : ' + num);
  5. // 输出 number : 16
  6. num后面的 1616进制, 16进制为
  7. 1 2 3 4 5 6 7 8 9 a b c d e f ==> 10

③parseFloat()

用途: 从数字位一直截到第一个小数点结束到第二个小数点,遇到非数字位直接返回前面的数字


   
     
     
     
     
  1. // 小栗子
  2. var num = "123.444.555"
  3. parseFloat(num);
  4. 输出 123.444
  5. num = '123.44abc';
  6. parseFloat(num);
  7. 输出 123.44
  8. num = 'abc44.123abc';
  9. parseFloat(num);
  10. 输出 NaN

④String() 

用途: 跟Number一样,致力于把()中的值转换为字符串


   
     
     
     
     
  1. // 小栗子
  2. var num = 123;
  3. String(num);
  4. 输出 '123'
  5. num = undefined;
  6. String(num);
  7. 输出 'undefined'
  8. num = null;
  9. String(num);
  10. 输出 'null'

⑤Boolean

用途: 除了被认为是false的六个值, 0 '' false undefined null NaN, 其他都是true


   
     
     
     
     
  1. // 小栗子
  2. Boolean( 0 | '' | false | undefined | null | NaN)
  3. // 输出是false

⑥demo.toString([radix])

 用途1:  radix如果不传值的话, 用途是将demo转换为数字类型


   
     
     
     
     
  1. // 小栗子
  2. var num = 123;
  3. num. toString();
  4. 输出 '123'
  5. num = undefined | null;
  6. num. toString();
  7. // 会报错 Uncaught TypeError: Cannot read property 'toString' of null

用途2: radix如果传值的话,是将demo转换为目标基底的值


   
     
     
     
     
  1. // 小栗子
  2. var demo = 123;
  3. var num = demo. toString( 10);
  4. 输出为 123
  5. num = demo. toString( 8);
  6. // 有一个好玩的
  7. // 先给你一个二进制的数,让你转换为16
  8. var num = 1010101010;
  9. var test = parseInt(num, 2); // 首先转换为10进制
  10. var tostring = test. toString( 16);
  11. 输出为 "2aa"

12.类型转化之隐式类型转换(它转换了你都不知道咋转换的,不过它转换的时候内部调用的是显式类型转换)

①isNaN()


   
     
     
     
     
  1. // 原理 isNaN里面首先会通过Number()转换,之后再与NaN进行比较
  2. // 小栗子
  3. isNaN( 'abc') 输出为 true
  4. isNaN( '123') 输出为 false

② ++  --  +(正)  -(负)


   
     
     
     
     
  1. // 提前调用Number(),'abc'虽然转换成了NaN,但是它的typeof是Number
  2. // 小栗子
  3. var a = 'abc';
  4. a++;
  5. a--;
  6. -a;
  7. +a;
  8. typeof(a); 输出为 "number"

③+ (加号)


   
     
     
     
     
  1. // 原理: 加号两边如果有string,就会调用String(),把不是字符串的转化成字符串然后连接
  2. var a = 'a' + 1; 输出为 'a1'

④- (减号) * / % 


   
     
     
     
     
  1. // 原理: 调用Number
  2. // 小栗子
  3. var a = '1' * '1';
  4. console. log( typeof(a) + " " + a);
  5. 输出 number 1

⑤ && || !

⑥ > <  >= <=


   
     
     
     
     
  1. // 原理: 两个值比较,一方有数字,另外一方转换为数字
  2. // 小栗子
  3. var a = '3' > 2;
  4. console. log(a);
  5. 输出 为 true

⑦ == !=


   
     
     
     
     
  1. // 小栗子
  2. var a = 1 == '1' 输出为 true
  3. var a = 1 == true 输出为 true
  4. undefined == null ==> 因为 undefinednull既不大于 0,也不小于 0,所以相等
  5. // 三等比较 从左到右比较
  6. 100 > 10 > 0 ? 输出为 true
  7. 首先 100 > 10 输出 true
  8. 接着 true > 0 ? 输出为 true
  9. // 特殊需要记忆
  10. NaN == NaN 输出 false 非数不等与自己,也不可能等于其他

⑧不发生类型转换 === 和 !==


   
     
     
     
     
  1. // 小栗子
  2. var a = 1 === '1' 输出为 false 因为 1 是number类型, '1'是string类型

注意: 在没有定义a的前提现,输出a,会报 a is not defined, 当且仅当输出 typeof(a)的时候回输出undefined,不会报错

13.函数

①函数声明


   
     
     
     
     
  1. function test( ) {}
  2. 关键字 函数名
  3. // 函数跟数组一样,栈里面是指向堆的地址,堆里面是内容
  4. // 无论是变量名还是函数名都要遵循"小头风的规则", 如下
  5. function theFirstName( ) {}
  6. console. log(theFirstName)
  7. 输出为 function theFirstName( ) {}
  8. // js不输出函数的地址,它输出地址指向那个房间的内容

②函数表达式

a. 命名函数表达式


   
     
     
     
     
  1. // 命名函数表达从语义上来说是再给变量赋值函数的时候,该函数有函数名
  2. // 小栗子
  3. var test = function abc( ) {
  4. console. log( 'a')
  5. }
  6. // abc 是函数名,test()会执行,abc()不会执行
  7. // 输出test.name 输出为abc

b.匿名函数表达式(重点,开发中我们都用这个)


   
     
     
     
     
  1. // 匿名函数表达式,从语义上来说是给变量赋值函数的时候,该函数没有函数名
  2. // 小栗子
  3. var test = function( ) {
  4. console. log( 'a');
  5. }
  6. // test.name 是 test

c.函数组成


   
     
     
     
     
  1. // 函数组成形式
  2. 必须有 function 函数名 ()括号 {}花括号

d.函数的参数(形参和实参)


   
     
     
     
     
  1. // 形参或者实参(可有可无), 真正的编程,有参数才更有意义
  2. // 小栗子
  3. function test( a, b ) { // 这里的a b是形参
  4. console. log(a + b);
  5. }
  6. test( 1, 2) // 这里的1 2 是实参
  7. 输出为 3

注意: js参数最强大的有点就是不限制位数,实参和形参位数可以不对等,实参可以比形参多,反之也可以


   
     
     
     
     
  1. // 形参比实参多
  2. // 小栗子
  3. function sum( a, b, c) {
  4. console. log(a + b + c)
  5. }
  6. sum( 1, 2)
  7. 输出是 NaN 因为 undefinedNumber转换的时候变成了 NaN
  8. 以上栗子中形参是a b c, 实参是 1 2
  9. //实参比形参多
  10. function sum( a, b) {
  11. console. log(a + b);
  12. }
  13. sum( 1, 2, 3);
  14. 输出为 3
  15. 以上栗子中a b是形参, 1 2 3是实参
  16. // 如何读取形参的长度呢
  17. // 只需要 函数名.length即可获取
  18. // 如何读取实参的长度呢
  19. // 只需要 arguments.length

下面有一个小测试: js实现任意数求和,任意数的意思是不规定你传入几个数字


   
     
     
     
     
  1. function sum( ) {
  2. var result = 0;
  3. var len = arguments. length;
  4. for( let i = 0; i < len; i++) {
  5. result += arguments[i]
  6. }
  7. console. log(result);
  8. }
  9. sum( 1, 5, 6, 7);
  10. 输出为 19
  11. // 测试 arguments[0] 和第一个实参是绑定关系吗
  12. function sum( a, b) {
  13. arguments[ 0] = 1;
  14. console. log(a);
  15. }
  16. sum( 2, 3);
  17. 输出为 1
  18. // 看来是绑定关系
  19. // 有个特殊情况
  20. function sum( a, b) {
  21. arguments[ 1] = 2;
  22. console. log(b);
  23. }
  24. sum( 1);
  25. 输出结果是 undefined
  26. // 此时发现,只有形参和实参有对应关系的时候,他们才有映射关系

e.返回值

return 必须写在函数里,它有两个作用,第一个作用是返回一个值,第二个作用是终止函数

终止函数,如果没有写在function,系统会自动在函数的最后加一个return undefined;

f.作用域


   
     
     
     
     
  1. var a = 123; // 此时的a是全局变量
  2. fucntion test( ) {
  3. var b = 345; // 此时的b是局部变量
  4. }
  5. 备注: 局部变量可以访问全局变量,全局变量不可以访问局部变量(里面可以访问外面的,外面的不能访问里面的)

14.递归 

  • 找规律
  • 找出口,找已知条件为出口

好处: 代码简洁 但是效率不高


   
     
     
     
     
  1. // 小栗子
  2. // 写n!(递归) 已知1! === 1
  3. // 分析 n! 为 n * (n - 1) * (n -2) * 一直乘到 1
  4. var n = window. prompt( "input");
  5. function mul( n) {
  6. if(n === 1) {
  7. return 1;
  8. }
  9. return n * mul(n- 1);
  10. }
  11. mul(n);

   
     
     
     
     
  1. // 小栗子
  2. // 写一个函数 实现斐波那契数列
  3. // 找规律 fb(n) = fb(n - 1) + fb(n - 2)
  4. function fb( n) {
  5. if(n == 1 || n == 2) {
  6. return 1;
  7. }
  8. return fb(n - 1) + fb(n - 2);
  9. }
  10. fb( 3);
  11. // 输出: 3

15.JS运行三部曲

  • 第一步::语法分析

系统会通篇扫描一遍,看程序有没有低级错误(少些")" "}"),如果发现有低级错误,系统将停止执行

  • 第二步:预编译

函数声明整体提升,变量 声明提升

imply global 暗示全局变量:任何变量,如果未经声明就赋值,此变量就归全局对象所有。


   
     
     
     
     
  1. // 小栗子
  2. a = 10;
  3. a 因为没有声明就进行赋值,相当于
  4. window. a = 10;
  5. // 又一个小栗子
  6. function test () {
  7. var a = b = 123;
  8. }
  9. test();
  10. console. log(b);
  11. console. log(a);
  12. 输出 123 a is not defined

  • 第三步:解释执行,一行代码一行代码地执行

16. 预编译(函数的预编译)

函数预编译发生在函数将要执行的前一刻

1. 首先创建AO(Activation Object)

2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

3.将实参和形参相统一

4.在函数体里面找函数声明,将函数声明的函数名作为AO的另一个属性名,函数声明的函数体作为该属性名的属性值


   
     
     
     
     
  1. // 小栗子
  2. function fn( a) {
  3. console. log(a);
  4. var a = 123;
  5. console. log(a);
  6. function a( ) {}
  7. console. log(a);
  8. console. log(b);
  9. var b = function( ) {};
  10. console. log(b);
  11. function d( ) {}
  12. console. log(d)
  13. }
  14. fn( 1);
  15. 输出 function( ){} 123 123 undefined function( ) {} function d( ){}

17. 预编译(全局的预编译)

1. 生成GO对象(Global Object)

2. 变量声明的变量名作为GO的属性名,值赋予undefined

3.找函数声明,值赋予函数体


   
     
     
     
     
  1. // 小栗子
  2. console. log(test);
  3. function test( test) {
  4. console. log(test);
  5. var test = 123;
  6. console. log(test);
  7. function test( ) {}
  8. }
  9. test( 1)
  10. // 输出 function test(test){} fucntion test() {} 123

   
     
     
     
     
  1. // 小栗子
  2. var global = 100;
  3. function fn( ) {
  4. console. log( global);
  5. }
  6. fn();
  7. 首先 GO {
  8. global: undefined ---> 100,
  9. fn: fucntion fn( ) {...}
  10. }
  11. 在代码执行到 fn() 前一刻
  12. AO{
  13. 啥也没有
  14. }
  15. console. log( global)时,由于 AO里面啥也没有,它会向 GO里面找 此时的 global100 输出 100

   
     
     
     
     
  1. // 稍微有点难的哦
  2. global = 100;
  3. function fn( ) {
  4. console. log( global); 此时找到fn函数内有变量声明 global undefined
  5. global = 200;
  6. console. log( global);
  7. var global = 300;
  8. }
  9. fn();
  10. var global;
  11. 输出 undefined 200 因为 AO中有 global

18.作用域精解

  • [[scope]] (对象隐式的属性)

每个JavaScript函数都是一个对象,对象的某些属性我们可以访问,有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中的一个,[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合 (执行期上下文,即预编译,函数预编译创建AO{} --> 函数执行完之后销毁AO{} ---> 当再次执行前函数预编译 ---> 创建AO{} ---> 用完就销毁了)

  • 作用域链

[[scope]] 中所存储的执行期上下文的集合,这种集合呈链式连接,我们把这种叫做作用域链

  • 查找变量

作用域链变量的查找是从作用域的顶端依次向下查找,在哪个函数中找一个变量就去哪个函数的作用域链里查找


   
     
     
     
     
  1. // 函数小栗子分析作用域链
  2. function a( ) {
  3. function b( ) {
  4. var bb = 234;
  5. a = 0;
  6. }
  7. var aa = 123;
  8. b();
  9. console. log(aa);
  10. }
  11. var glob = 100;
  12. a();

当代码被执行的时候,创建GO对象,并将GO放到[[scope]]作用域顶端, 执行全局预编译,将glob变量和函数a提升到GO中,当执行到a()的前一刻,a函数执行函数预编译,创建AO对象,AO放到[[scope]]顶端,GO在AO下面,此时aa变量和b函数提升,放到AO对象中,在执行b()函数的前一刻,b函数执行函数预编译,创建一个新的AO对象,bb变量提升,a变量放到最开始的GO对象中,此时的AO放到最前面,下面依次是a函数的AO和全局的GO对象,如下图

a函数执行的时候:

b函数执行的时候:

总结:

当b函数执行完的时候,b函数回到定义的状态,销毁自己的AO(只能销毁自己的AO),剪短第三张图中0与AO的线,当b函数再次被执行的时候,会重新产生一个AO放到[[scope]]顶部,当b执行完,a函数内部代码被执行完,a函数剪掉第二张图0与AO的线,a会回到定义的状态。

如果a不执行,b就不会被定义。

19.闭包

  • 现象

当内容函数被保存到外部时,将会产生闭包,导致原油作用域链不释放,造成内存泄露。

  • 闭包的作用 实现公有变量


   
     
     
     
     
  1. // 实现一个累加器 使用闭包
  2. function add( ) {
  3. var count = 0;
  4. function demo( ) {
  5. count++;
  6. console. log(count);
  7. }
  8. return demo();
  9. }
  10. var counter = add();
  11. counter(); // 输出1
  12. counter(); // 输出2
  • 可以做缓存


   
     
     
     
     
  1. // 使用闭包做一个缓存
  2. function test( ) {
  3. var num = 100;
  4. function a( ) {
  5. num++;
  6. console. log(num);
  7. }
  8. function b( ) {
  9. num--;
  10. console. log(num);
  11. }
  12. return [a, b];
  13. }
  14. var myArr = test();
  15. myArr[ 0](); // 输出101
  16. myArr[ 1](); // 输出100

备注:在一个函数中,有多个子函数的话,这几个子函数公共指向同一个复函数产生的作用域链,即父函数的

test define test [[scope]] 0 GO 

test doing test [[scope]] 0 AO  1 GO

20.立即执行函数

  • 定义:凡是写的函数只想被执行一次,执行完一次后被销毁的函数叫做初始化功能函数,即立即执行函数


   
     
     
     
     
  1. // 立即执行函数不传参格式
  2. ( function( ) {
  3. var a = 123;
  4. var b = 234;
  5. console. log(a, b);
  6. }())
  7. // 输出123 234
  8. // 传参格式
  9. ( function( a, b, c) {
  10. console. log(a, b, c);
  11. }( 1, 2, 3))
  12. // 輸出1 2 3
  • 两种格式:

  • (function() {}()) ()在里面 ---> 建议这种(w3c标准)

  • (function() {})() ()在外面

注意:只有表达式才能被执行


   
     
     
     
     
  1. // 小栗子
  2. fucntion test( ) {} () ---> 不会被执行,因为test是函数声明不是函数表达式
  3. 123; 234; 123, 234 ----> 可以叫做表达式
  4. var test = function( ) { console. log( 'a')} () ----> 能执行 输出a

被执行函数执行的表达式就成了立即执行函数,并忽略函数名引用


   
     
     
     
     
  1. // 小栗子
  2. var test = function( ) { console. log( 'a') };
  3. 执行 test() test --> 输出为 function
  4. 当写成 var test = fucntion( ) { console. log( 'a')} ()
  5. 执行完之后 test ---> undefined
  • 立即执行函数的几个题


   
     
     
     
     
  1. // 给ui下的四个li绑定一个事件,输出每个li的位置
  2. function test( ) {
  3. var liCollection = document. getElementsByTagName( 'li');
  4. var len = liCollection. length;
  5. for( var i = 0; i < len; i ++) {
  6. ( function( j) {
  7. liCollection[j]. onclick = function( ) {
  8. console. log(j + 1)
  9. }
  10. }(i))
  11. }
  12. }
  13. // 如果没有立即执行函数,每个li输出的i都是li标签的数量,因为当li被点击的时候,li函数的GO作用域中的i已经变成了len
  14. // 求一个字符串的字节长度(提示:字节串有一个方法,charCodeAt();一个中文占两个字节,一个英文占一个字节) charCodeAt返回指定位置的字符串的Unicode编码,这个返回值是0~65535之间的整数。(当返回值<= 255时为英文,>时就是中文)
  15. function retByteSlean( target) {
  16. if( typeof(target) !== "string") {
  17. return;
  18. }
  19. var len = target. length,
  20. count = len;
  21. for( var i = 0; i < len; i++) {
  22. if(target[i]. charCodeAt() > 255) {
  23. count++;
  24. }
  25. }
  26. return count;
  27. }

21.逗号运算符

  • 原理: 

逗号表达式会先计算表达式前面的值,再计算后面的值,最后打印后面的值。


   
     
     
     
     
  1. // 小栗子
  2. var a = ( 2 + 1, 2 - 1);
  3. 输出 1

22.对象

  • 定义:生活中的任何东西都可以抽象为对对象,对象有属性和方法,我们可以实现对对象的增删改查

   
     
     
     
     
  1. // 对对象的增删改查
  2. var obj = { name: 'zl', age: 18};
  3. 增 对象 + . + 属性名 + : + 属性值 --> obj. lastName = 'lei'
  4. delete + 对象.属性名 ---> delete obj. name
  5. 改 obj.原属性名 = 需要改的属性值 ---> obj. age = 17
  6. 查 obj.属性名, 原对象没有该属性名,返回 undefined ---> obj. name 输出 zl obj. height 输出 undefined
  • 对象创建的几种方式

   
     
     
     
     
  1. // 第一种 对象字面量/对象直接量
  2. var obj = {...}
  3. // 第二种 (通过构造函数构造对象)
  4. 1. 系统自带 Object构造函数,通过 Object来创建对象, Object系统相当于一个工厂,它可以批量地生产对象,生产出来的对象一毛一样,但是每个对象都相互独立,系统自带其他构造函数,如 ArrayNumber
  5. new Object(),系统会给你一个真正的对象,js中的对象相对于 Java和C++的对象更加灵活,js中的对象更像人的出生,系统自带 Object的使用
  6. var obj = new Object();
  7. obj. name = 'zl';
  8. 2.自定义的构造函数
  9. function Person( ) {}
  10. 因为构造函数跟普通函数看起来没什么区别,所以构造函数在命名时要遵循大驼峰规则(单反是单词首字母都需要大写)这样来区别
  11. |
  12. —— 小栗子
  13. function Car( color) {
  14. this. height = 1400;
  15. this. weight = 1000;
  16. this. lang = 4900;
  17. this. color = color;
  18. }
  19. var car1 = new Car( 'red');
  20. var car2 = new Car( 'green');
  21. |
  22. _ 解析
  23. Car 相当于一个车间,给同一个车型车刻了一个模子,color是可选的同款车型的不同颜色,height、weight、lang是该款车型不变的配置, Car生产出来的车相互独立,互不影响(互不影响就可以用来做原型链的继承)
  • 构造函数内部原理

   
     
     
     
     
  1. 1. 在函数体最前面会有一个隐式的 this对象
  2. function Student( ) {
  3. // var this = {};
  4. }
  5. var this = {} 相当于在 AO中加入 this属性名和{}属性值 AO{ this: {}}
  6. 刚开始的时候这个 this指向 window,当 new 该函数的时候, this指向__proto__
  7. 2.执行 this. xxx = xxx
  8. 3.隐式返回 this ==> 这一步是 new之后返回的
  9. function Student( name, age) {
  10. this. name = name;
  11. this. age = age;
  12. // new之后 代码执行到这里相当于 AO{this: {name: name, age: age}}
  13. // 最后隐式返回this 如果你强制return {} 会替换return的this, 如果你return的是原始值,
  14. // 不会影响this对象的返回
  15. }
  16. // 如果不执行new,就是普通的 Student(1,2) 这里的this指向window,这里的name和age是给window上赋值,执行window.name window.age 分贝输出1 和 2

23.包装类(原始值有对象方式的创建方式)

在JavaScript中数字有两种个数字,字符串分两种字符串,布尔值分为两种布尔值,原始值数字才是原始值,原始值字符串才是原始值,这里还有非原始值数字和非原始值字符串,new Number(xxx) 默认是Number {0} 、new String(xxx)  String {""}、 new Boolean() 默认是Boolean {false} 

注意:undefined和null没有包装类


   
     
     
     
     
  1. // 包装类有自己的属性和方法
  2. var num = new Number( 123);
  3. num. abc = "ac";
  4. console. log(num. abc); // 输出 “ac”
  5. num. fn = function( ) { console. log( 123) };
  6. num. fn() // 输出 123
  7. // 但是原始值本身没有属性和方法,也不能加属性和方法,也不能访问属性和方法
  8. var num = 3;
  9. num. length = 4; // 原始值num是没有length属性的,它会转换为new Number(3).length = 4,执行完这段代码之后Number销毁
  10. console. log(num. length); // 此时又重新转换为new Number(3),此时输出length为undefined。
  • 包装类面试题

   
     
     
     
     
  1. // 面试题
  2. var str = "abc";
  3. str += 1;
  4. var test = typeof(str);
  5. if(test. length === 6) {
  6. test. sign = "typeof返回的值可能是string"; // 转换为包装类之后销毁
  7. }
  8. console. log(test. sign) // 再次转换为包装类 因为new String("test")没有sign这个属性,输出为undefined 原始值没有方法和属性
  9. // 输出为undefined

24.原型

定义:原型是function对象的一个属性,它定了构造函数制造出来的对象的公共祖先,通过构造函数生产的对象可以继承该原型的属性和方法,原型也是对象。

  • 原型的特点:

我们创建出来的对象,都会继承了父级的prototype,他不仅有自己定义的属性,还有“他爹”(原型上)的属性,当我们构造出来的对象有公共的不改变的属性,我们可以把这些不变的属性到原型上面


   
     
     
     
     
  1. // 小栗子
  2. function Car( color, ower) {
  3. this. color = color;
  4. this. ower = ower;
  5. this. height = 1400;
  6. this. weight = 4900;
  7. }
  8. var car = new Car( "red", "lei.zhou");
  9. // 每次new Car()都会走一遍 this.height this.weight, 而这两个属性是不变的,可以直接放到Car的原型上面
  10. Car. prototype = { height: 1400, weight: 4900}
  • 原型的增删改查(构造函数的原型本职上是对象,它的增删改查就是对象的增删改查)

   
     
     
     
     
  1. // 小栗子
  2. Person. prototype. lastName = "lei";
  3. function Person (name) {
  4. this. name = name;
  5. }
  6. var person = new Person( "张");
  7. // 原型的增
  8. Person. prototype. firstName= "zhou";
  9. // 原型的删
  10. delete Person. prototype. firstName; 返回 true
  11. // 原型的改
  12. Person. prototype. lastName = "leizi";
  13. // 原型的查
  14. Person. prototype. firstName; 删除 "zhou"
  • 原型的写法

   
     
     
     
     
  1. // 原型的两种写法
  2. 第一种 XX. prototype. xx = xx
  3. Person. prototype. name = 'buhaoba';
  4. 第二种 XX. prototype = { xx: xx}
  5. Person. prototype = { name: "buhaoba"};
  • 原型的constructor(构造器)

   
     
     
     
     
  1. // 查看对象的构造函数
  2. function Car( ) {}
  3. var car = new Car();
  4. console. log(car. constructor); // 输出为 function Car(){}
  5. // car.constructor有时候也会找错生产自己的工厂
  6. function Person( ) {}
  7. car. prototype = {
  8. constructor: Person
  9. }
  10. function Car( ) {}
  11. var car = new Car();
  12. console. log(car. constructor); // 输出为Person
  • 原型的__proto__

   
     
     
     
     
  1. Person. prototype. lastName = "abc";
  2. function Person( ) {}
  3. var person = new Person();
  4. console. log(person);

从上图可以知道输出person.name时会沿着__proto__找到Person.prototype,__proto__跟Person.prototype指向同一个地址,他两都是引用值,在原型中找对应的属性,Person.prototype属性有lastName和constructor,constructor这个指向function Person() {},Person.prototype也有自己的__proto__,这个__proto__指向Object.prototype

  • 修改原型对构造出来对象的影响

   
     
     
     
     
  1. Person. prototype. name = "sunny";
  2. function Person( ) {}
  3. var person1 = new Person();
  4. Person. prototype = { name: 'abc'};
  5. console. log(person1. name); // 输出为"sunny"
  6. var person2 = new Person();
  7. console. log(person2. name); // 输出为"abc"
  8. Person. prototype. name = "sunny";
  9. function Person( ) {}
  10. var person = new Person();
  11. Person. prototype. name = 'abc';
  12. console. log(person. name); // 输出为"abc"

为啥会出现以上这种情况呢,原因竟然是:

第一种:打印person1.name 沿着__proto__找Person.prototype,此时的Person.prototype的指向为stack0001,这里面的name为“sunny”,所以输出“sunny”;打印person2.name 沿着__proto__找Person.prototype,Person.prototype重新赋值一个对象,指向发生了改变,此时的Person.prototype的指向为stack0002,这里面的name为“abc”,所以输出“abc”。

第二种:打印person.name 沿着__proto__找Person.prototype,此时的Person.prototype的指向为stack0001,这里面的name从“sunny”变成了“abc”,指向还是之前的 stack0001,只是内容改变了,所以输出“abc”。

25.原型链

  • 如何构成原型链呢?

   
     
     
     
     
  1. Grand. prototype. lastName = "lei";
  2. function Grand( ) {
  3. this. age = 1000;
  4. }
  5. var grand = new Grand();
  6. Father. prototype = grand;
  7. function Father( ) {
  8. this. height = 123;
  9. }
  10. var father = new Father();
  11. Son. prototype = father;
  12. function Son( ) {
  13. this. weight = "henqin";
  14. }
  15. var son = new Son();
  16. console. log(son);

  • 原型链属性的增删改查

   
     
     
     
     
  1. // 原型链的增
  2. 构造函数名. prototype. xx = xx;
  3. Father. prototype. happyState = true;
  4. console. log(son. happyState); 输出为 true
  5. // 原型链的删
  6. delete 构造函数名. prototype. xx
  7. delete Father. prototype. happyState;
  8. console. log(son. happyState); 输出为 undefined
  9. // 原型链的改 (改值的时候不要直接等于一个引用值,这样会让原型链断裂)
  10. 构造函数名. prototype.原有属性名= xx;
  11. Father. prototype. age = 15;
  12. console. log(son. age ); 输出为 15
  13. // 原型链的查
  14. 从儿子级一直到祖先查,如果祖先有和自己一样的属性,输出自己的
  • 绝大多数对象最终都会继承自Object.prototype

   
     
     
     
     
  1. // 小栗子
  2. function Person( ) {}
  3. var person = new Person;
  4. person. toString(); 输出 “[object Object]”

为啥会输出呢,是因为Person的__proto__指向Object.prototyepe,Object.prototyepe里有toString这个属性

  • Object.create(原型) 构造对象 绝大多数对象最终都会继承自Object.prototype,为什么说绝大多数呢?看以下内容就可明白

   
     
     
     
     
  1. var obj = Object. create( "原型 可以自己指定");
  2. var prop = {
  3. name: 123
  4. }
  5. var obj1 = Object. create(prop);
  6. console. log(obj1);

如果create里面传值为null时,他就不继承自Object.prototype

 


   
     
     
     
     
  1. var obj = Object. create( "原型 可以自己指定");
  2. var prop = null;
  3. var obj1 = Object. create(prop);
  4. console. log(obj1);

  • 重写系统定义函数定义的原型上的方法

   
     
     
     
     
  1. var num = 123;
  2. num. toString(); 输出为“ 123
  3. 为啥会输出字符串 123呢,因为 Number. prototype原型上面有 toString = function( ) {}
  4. Number. prototype中没有的属性才会找 Object. prototype上的属性
  5. // 包装类(Number String Boolean) 和 Array Object都有自己的toString方法
  6. 调用的方法不同,输出的结果也不同
  7. Object. prototype. toString. call( 123) 输出 "[object Number]"

26.改变this指向 (call 和 apply)


   
     
     
     
     
  1. function Person( name, age) {
  2. this. name = name;
  3. this. age = age;
  4. }
  5. var person = new Person( "zhoulei", 18);
  6. var obj = {};
  7. Person( 1, 2); 相当于 Person. call( null, 1, 2);

 call 和 apply进行改变this指向的芳芳详解


   
     
     
     
     
  1. // call 改变this指向 第一个参数是this,this需要指向的函数 之后的参数是需要进行赋值的参数,用‘,’分隔传递
  2. function a( name, age) {
  3. console. log( 123);
  4. this. name = name;
  5. this. age = age;
  6. }

你可能感兴趣的:(javascript,前端,vue.js)