js一般规则

前端面试


  1. 0.1 + 0.2 是否== 0.3
    1. js的Number类型遵循IEEE 754标准,要用IEEE 754来表示0.1,需要先将0.1转化成二进制,再将二进制转换成科学计数法,最后再转换为 IEEE 754 标准表示。而0.1和0.2二进制都是无限循环小数,由于数据存储大小的限制会将超出52位的数截取掉,因此造成了精度丢失。解决方法是使用ES6中提供的Number.EPSILON属性,它定义了一个大小为2-52的机器精度,如果误差小于该机器精度,视为相等。

      基本点


  1. js严格区分大小写
  2. js可以不写分号,但

    1. ①多行代码写一行必须有分号。<br />②**标准写法:一行代码写一行,且每一行都要加分号**。
  3. 如果出错,F12->console查看错误,进一步打开source->添加断点和要监视的变量(右键)->f5进入调试模式->f9开始调试->同时不断查看console和source。

  4. 可正常计算的范围:小数点前16位、小数点后16位
  5. js语言是单线程的
    1. 因此JavaScript的所有网络操作、浏览器事件都只能异步执行。
  6. js语言是异步编程语言
    1. 异步编程语言不会像同步编程那样一个事件一个事件的执行,比如在setTimeOut设置的等待时间内CPU会继续执行别的任务,而不会完全停止。
  7. 一个等号是赋值操作,==先转换类型再比较,===先判断类型,如果不是同一类型直接为false。

js的ASI机制(Automatic Semicolon Insertion)


什么是ASI

按照 ECMAScript 标准,一些 特定语句(statement) 必须以分号结尾。分号代表这段语句的终止。但是有时候为了方便,这些分号是有可以省略的。这种情况下解释器会自己判断语句该在哪里终止。这种行为被叫做 “自动插入分号”,简称 ASI (Automatic Semicolon Insertion) 。

本质

实际上分号并没有真的被插入,这只是个便于解释的形象说法。

js的书写位置


  1. 内嵌式js
  • 直接写在标签里,一般是写在body标签的下面,目的是加快页面的响应速度。(存疑)
  1. 外部式js
  • 用script标签引入外部js:<script src="1.js"></script>
  • 引用了外部式js,里面写的js会被浏览器自动忽略
    1. <body>
    2. <p onclick="alert('有效')">点我</p>
    3. </body>
    4. <script src="exer-1.js">
    5. document.write('无效')
    6. </script>
    7. <script>
    8. //重建script有效
    9. document.write('有效')
    10. </script>
  1. 行内式js
  • 可以写在标签的onclick属性上<p onclick="alert('不听话啊')">不要点我</p>
  • 可以写在a标签的href属性上 <a href="javascript:alert('别走')";
  • 不推荐,因为造成了html和js代码耦合。

    作用域


  • 定义
    • 作用域表示变量的作用区域
  • 分类
    • 全局作用域
      • 存在位置
        • 直接编写在 script 标签之中的JS代码
        • 一个单独的js文件中
      • 存在时间
        • 在页面打开时创建,在页面关闭时销毁
      • 特点
        • 全局作用域中有一个全局对象window(代表的是一个浏览器的窗口,由浏览器创建),在全局作用域创建的变量都会作为window对象的属性保存;所有创建的函数都会作为 window 对象的方法保存。
      • 全局变量
        • 定义
          • 在全局作用域由var创建的变量
          • 在函数作用域直接赋值的变量(不建议使用)
        • 存在时间
          • 全局变量只有在浏览器关闭的时候才会销毁,比较占用内存资源
        • 特点
          • 全局变量在任何地方都可以使用
    • 函数作用域/局部作用域
      • 存在位置
        • 函数范围
      • 存在时间
        • 调用函数时创建函数作用域,函数执行完毕销毁
      • 特点
        • 每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的
      • 局部变量
        • 定义
          • 在函数作用域由var声明的变量
          • 函数的形参也是局部变量
        • 存在时间
          • 只能在函数内部使用,当其所在代码块被执行时,会被初始化;当代码块执行完毕就会销毁,因此更节省节约内存空间。
        • 特点
          • 全局作用域无法访问局部变量
    • 作用域链
      • 定义
        • 内部函数访问外部函数的变量,会采用链式查找的方法,这种结构就叫作用域链。
      • 原则
        • 就近原则 ```javascript //知识点1:全局变量会作为window对象的属性保存,但局部变量不会被作为局部对象的属性保存。 var num = 10; console.log(window.num);//10

func = function(){ var num = 20; } func(); console.log(window.num);//10 console.log(func.num);//undefined

  1. ```javascript
  2. //知识点1:全局变量会作为window对象的属性保存,但局部变量不会被作为局部对象的属性保存。
  3. //知识点2:作用域链:就近原则
  4. var num = 30;
  5. var fn = function(){
  6. var num = 10;
  7. fn.num = 20;
  8. (function(a){
  9. console.log(a);
  10. })(num)//会一级一级往上面找,num = 10
  11. }
  12. fn();//10
  13. console.log(fn.num);//20
  14. //-----对比-------
  15. //知识点:局部作用域可以访问全局变量
  16. var num = 30;
  17. var fn = function(){
  18. fn.num = 20;
  19. (function(a){
  20. console.log(a);
  21. })(num)//会一级一级往上面找,num = 30
  22. }
  23. fn();//30
  24. console.log(fn.num);//20

image.png

  • 块级作用域
    • 存在位置
      • ES6之前:()即大括号 ——-> 使用一个立即执行的匿名函数 ```javascript

function test(){ (function(){ for(var i=1;i<5;i++){ alert(i); } })(); console.log(i);//Uncaught ReferenceError: i is not defined } test();

  1. - let创建块级作用域
  2. <a name="C9Vpr"></a>
  3. ## js的字面量和变量
  4. ---
  5. <a name="mbjxb"></a>
  6. ### 字面量
  7. - 字面量就是常量,一般是全大写表示 `var INF = 999999999;`
  8. <a name="u2rFR"></a>
  9. ### 变量
  10. - 全局变量和局部变量
  11. - 全局变量
  12. - 定义
  13. - 除了在函数里面**用var定义的变量**都是全局变量
  14. - 生命周期
  15. - 在页面打开时创建,关闭时销毁
  16. - windows对象一样
  17. - js里,所有的全局变量都是jswindow对象的属性;所有的全局函数都是jswindow对象的方法
  18. - windows对象:js里面给我们创建好了的对象,表示浏览器的一个窗口
  19. - 局部变量
  20. - 定义
  21. - 函数里面**用var定义的变量**是局部变量
  22. - 生命周期
  23. - 挂靠在函数上面
  24. - 在函数使用时被创建,使用完时销毁
  25. - 函数里面使用变量的时候,优先在函数里面找,如果有就用,如果没有就向上找。
  26. - 如何在函数里面使用全局变量?-通过windows对象来访问,比如windows.a
  27. ```javascript
  28. function hello(){
  29. console.log(a);
  30. var a = 11;//局部变量
  31. d = 15;//全局变量
  32. }
  33. hello();
  34. console.log(d);
  35. for(var i = 0; i < 3; i++){
  36. console.log(i);//i是全局变量
  37. }
  • 默认初始值
    • 变量如果只声明不被赋值,初始值是undifined,console.log(变量)会看到undifined

  • 变量命名

    • 命名可以字母、数字、下划线_、$(数字不能在最前面,其它的可以)
    • 标识符不能使用js中的关键字或者保留字
    • 一般采用驼峰命名法 var myAge = 20;
    • js存储的底层字符编码是unicode,所以UTF-8里面支持的字符都可用作标识符 var 中文= 123;

      但一般不这么干

      js数据类型


  • 控制台查看数据类型

    1. `console.log(typeof str);`

    基本数据类型

  • 基本数据类型都可以用Object封装一下,变成一个对象,而且它们会和function对象一样,比普通object功能更强大,因为它们本身是有含义的。

    • 它们的原型会先是Number/String/function自己的构造函数….,然后才是Object
    • 即使这个对象有了很多别的属性,但对于本身的含义,可以和不作为Object的时候一样 直接使用
      1. var num = new Object(123);
      2. num.age = 13;
      3. var n = num + "123";
      4. console.log(n);//123123(打印出来的没有引号,查看类型肯定是字符串)
  1. String:字符串
    1. var str = '我爱你,我的家!';
    2. console.log(typeof str);//--->string
    3. var char = str[0];
    4. console.log(typeof char);//--->string
  • 单引号和双引号都可以,如果是单引号就只能在内部用双引号,反之是双引号就只能在内部用单引号,也可以用转义字符:var str = "她说:\"我爱你,我的家。\"";
  • 转义符 :\(反斜杠) 、\t(制表符)、\n(换行符)、\”(双引号)、\’(单引号)、\r(回车)
    • 制表符,理解为tab,两个字符缩进
  • var char = str[0],的字符类型还是string
  1. Number:数字—->整数、小数(浮点数) ```javascript var num1 = 123; console.log(typeof num1);//number

var num2 = Number.MAX_VALUE; console.log(num2);//1.7976931348623157e+308

var num3 = Number.MIN_VALUE; console.log(num3);//5e-324

var num4 = Infinity;//正无穷大 console.log(num4);//Infinity

var num5 = -Infinity;//负无穷大 console.log(num5);//-Infinity

  1. - Number 类型是一个 双精度 64 位二进制格式 IEEE 754 值(-(2^53 1) 2^53 1 之间的数字)
  2. - js的小数做计算的时候,可能会出现不精确的情况。
  3. 原因是:在10进制里面,1/3是没有办法被精确表示的,0.3333,在二进制里面,1/10也没有办法被精确表示,是一个无限循环小数。<br /> ** 解决方法:先转化成整数做计算,再转化为小数**
  4. 3. Boolean:布尔值
  5. ```javascript
  6. var bool = true;
  7. console.log(typeof bool); //boolean
  1. Null:空值
    1. var v1 = null;
    2. console.log(typeof v1); //object
  • Null表示是空值
  • Null这个数据类型里面只有一个值,就是null
  • 这个null值表示的是一个空的对象
  1. Undefined:未定义(字面意思)
    1. var v;
    2. console.log(typeof v);//undefined
  • 它表示变量定义/声明了没有赋值,但是变量肯定定义了!!,没有定义会报错。

    引用数据类型

    object

    基本概念

  • 定义

    • 对象:理解为一种容器,这个容器里可以装各种数据类型。
  • 性质
    • 在js中,除了5种基本的数据类型,其它都是对象。
  • 对象分类
    • 内建对象:内部已经创建好的对象(内部就是es,es就是js的一个标准)
      • 比如:Math、Number、String、Function、Array…..
      • var a = Math.sqrt(9);
    • 宿主对象:表示js运行环境提供的对象(运行环境:浏览器)
      • 比如:console、document、window…..
    • 自定义对象:我们自己定义的对象 ```javascript //使用Object构造函数创建对象 var human = new Object();//在堆中new出一片内存空间,并生存一个内存地址赋给引用的变量 human.name = “张十三”; human.age = 18;

console.log(typeof human);//object

  1. <a name="Zx3IB"></a>
  2. #### new运算符
  3. 1. 创建一个新对象;
  4. 1. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
  5. 1. 执行构造函数中的代码(为这个新对象添加属性) ;
  6. 1. 返回新对象。
  7. <a name="MMYHb"></a>
  8. #### 对象的基本操作
  9. 1. 点号版
  10. 1. 创建
  11. `var 对象名 = new Object();`
  12. 2. 新增、修改
  13. `对象名.属性名 = 属性值;`
  14. 3. 访问
  15. `对象名.属性名`<br />ps:访问对象没有的属性,不会报错,会返回undefined
  16. 4. 删除
  17. `delete 对象名.属性名`
  18. 2. 中括号版
  19. - 底层是中括号
  20. ```javascript
  21. human[123] = 'aaa';
  22. console.log(human[123]);
  23. //这是点号不能进行的操作,因为123是不符合规范的变量名。
  24. //因此中括号可以进行一些特殊的字符操作
  25. var a = 123;
  26. console.log(human[a]);
  27. a = 456;
  28. console.log(human[456]);
  29. //中括号的属性名可以是变量,这也是点号不能进行的操作

创建对象的字面量形式

JSON的属性名称要求以“双引号”包裹,所有只有*行是json格式的字面量表达

  1. var zhubajie = {};//创建空对象就这么写的,效率高
  2. zhubajie.name = "猪八戒";
  3. zhubajie.age = 9;
  4. //主要是下面这种,因为效率高
  5. var zhubajie = {name:"猪八戒",age:"9"};//属性名加不加引号都可以
  6. var zhubajie = {'name':"猪八戒",age:"9"};//单引号√
  7. var zhubajie = {"name":"猪八戒",age:"9"};//双引号√ *
  8. var zhubajie = {'name':"猪八戒",age:9};//去掉属性值的引号,更改了其数据类型

json

  • 全称
    • javascripte object notation(js的对象符号)
  • 作用
    • 通信,前后端交互(比xml多)
  • 语法:

    • {属性名:属性值,属性名:属性值,属性名:属性值}
    • image.png
    • 属性名:属性值——->称为键值对,键值对之间用逗号分隔开

    • 用工厂模式创建对象

  • 作用:封装代码,便于复用。

  • 本质:使用函数封装代码 ```javascript //1、定义一个有名字的函数 var createPeople = function(name, age){ //2、该函数内部要结合传参,定义一个初始对象 var people = { name:name, age:age, introduction:function(){
    1. console.log("我是" + this.name + ",我今年" + this.age + "岁了。")
    } } //3、返回这个对象 return people; } var xiaozhang = createPeople(“xiaozhang”, 21); var xiaowang = createPeople(“xiaowang”, 18);

xiaozhang.introduction();//我是xiaozhang,我今年21岁了。 xiaowang.introduction();//我是xiaowang,我今年18岁了。

  1. <a name="caEl4"></a>
  2. #### 用构造函数创建对象
  3. - 好处
  4. - 可以标注对象
  5. - 特点
  6. - 创建时:**函数名的首字母大写(约定俗成)**
  7. - 使用时:必须使用new关键字
  8. - 本质
  9. 1. 它会创建一个空对象{};
  10. 1. 链接空对象的原型到构造函数的原型;
  11. 1. 将空对象作为this的上下文,并执行构造函数;
  12. 1. 返回this
  13. ```javascript
  14. function myNew(f,...args){
  15. //创建空对象
  16. let obj=Object.create(null);
  17. //将空对象指向构造函数的原型链
  18. Object.setPrototypeOf(obj,f.prototype);
  19. //将this绑定给obj,并执行函数
  20. let r=f.apply(obj,args);
  21. //返回函数值,若不是对象返回obj
  22. if (typeof(r) == "object"){
  23. return r;
  24. }else{
  25. return obj;
  26. }
  27. }
  28. //构造函数
  29. function f(name,age){
  30. this.name=name;
  31. this.age=age;
  32. }
  33. var pa=myNew(f,'张三','26');
  34. console.log(pa)
  • 怎么判断一个对象是不是一个类的实例
    • instanceof
  • 不足
    • 浪费资源:每次使用构造函数创建对象的时候,构造函数中的方法(比如introduction方法)都会被创建,如果我们要创建10000个对象,那么introduction方法也就会创建10000次。
    • 怎么解决?
      • 法一:把构造函数中的方法放到全局,这样虽然解决了资源浪费的问题,但是会污染全局的方法,所以不推荐。
      • 法二:原型(即定义对象的prototype) ```javascript function People(name, age){ this.name = name; this.age = age; this.introduction = function(){ console.log(“我是” + this.name + “,我今年” + this.age + “岁了。”) } } var xiaozhang = new People(“xiaozhang”, 21); var xiaowang = new People(“xiaowang”, 18); console.log(xiaozhang);

xiaozhang.introduction(); xiaowang.introduction();

console.log(xiaowang instanceof People);//true

  1. <a name="PO9i3"></a>
  2. #### 对象里面套对象
  3. - 对象里面还可以是对象
  4. - 本质:对象里的一个属性名,可以对应任何一种数据类型
  5. ```javascript
  6. var baba = new Object();
  7. baba.name = '张力';
  8. baba.age = '48';
  9. var nver1 = new Object();
  10. nver1.name ='Anna';
  11. nver1.age = '20';
  12. baba.haizi1 = nver1;
  13. console.log(baba.haizi1.age);

对象里是否有这个属性

  • 针对单个属性
  • 只检查能不能访问到,不能检查到底属不属于这个对象
    1. console.log('age' in nver1);//true
    2. console.log(age in nver1);//报错:Uncaught ReferenceError: age is not defined
    3. //打了引号才是属性,没有引号代表一个变量,而age变量根本没有定义
    4. var age = 'age';
    5. console.log(age in nver1);//true
    遍历属性-包含继承属性在内的所有可枚举的属性名 ```javascript var zhubajie = new Object(); zhubajie.name = “猪八戒”; zhubajie.age = 18; zhubajie.saorao = function(){ console.log(“我骚扰了嫦娥,被贬下凡间”); }

for(var i in zhubajie){ console.log(i);//i分别是 “name”、”age”、”saorao” console.log(zhubajie[i]); }

  1. 获取属性-只包含自身的可枚举的属性名
  2. ```javascript
  3. var obj1 = {
  4. name:"anna",
  5. age:13
  6. }
  7. var arr = Object.keys(obj1);
  8. //arr = ["name", "age"]

对象里的函数

对象里的函数一般称为方法,但实际是一回事,都是封装代码,便于代码的复用

  1. //1、使用Object构造函数创建对象-->赋值表达式
  2. //好像这就是,用“赋值表达式创建函数”这种方法存在的意义,因为要把它赋给一个对象/对象属性。
  3. var zhubajie = new Object();
  4. zhubajie.name = "猪八戒";
  5. zhubajie.age = 18;
  6. zhubajie.saorao = function(){
  7. console.log("我骚扰了嫦娥,被贬下凡间");
  8. }
  9. zhubajie.saorao();
  10. zhubajie.benpao = new Function("console.log('我在奔跑')");
  11. zhubajie.benpao();
  12. //2、json形式/字面量形式
  13. var action = {
  14. func:function(){
  15. console.log("我是func");
  16. },
  17. name:'猪八戒'
  18. }
  19. action.func();//"我是func"

function

写法

  • function 函数名([参数1, 参数2, …]){

    1. ...函数体...<br /> }

    ```javascript //1、用声明的方式创建对象 function minOfTwo(a, b){//形参:函数里面定义的变量 if(a < b){ console.log(a); }else{ console.log(b); } }

minOfTwo(1,2);//实参:(给形参注入灵魂)使用时传递给函数的实际参数

  1. <a name="ZmGCF"></a>
  2. #### 实参和形参
  3. - 形参:函数里面定义的变量,用于接收实参传递过来的值。
  4. - 实参:(给形参注入灵魂)使用时传递给函数的实际参数
  5. - 联系
  6. - 实参会一一传递给形参,多余的参数会被丢弃
  7. - 如果形参没有接收到实参,就是undefined(相等于声明不赋值)
  8. <a name="r3MIe"></a>
  9. #### 返回值
  10. - return后面的语句不会被执行
  11. - 如果不写return语句或者“return;”(即不自定义任何返回值),会返回undefined
  12. <a name="MGfCn"></a>
  13. #### 函数也是对象
  14. 有对象的一切特点,只不过是功能更加强大的对象,函数里面可以封装代码
  15. ```javascript
  16. function hello(){
  17. console.log("欢迎");
  18. }
  19. hello.age = 13;
  20. console.log(hello);//打印函数代码
  21. console.log(hello.age);//13
  22. console.log(hello.name);//hello (name是“通过声明定义的函数”的自带属性)

image.pngimage.png

创建函数的其它几种写法

  • 不论用以下哪种方法创建函数,当执行到这一行时,编译器都只会先声明它(一整坨),当确定执行的时候才会进入执行。 ```javascript //2、用创建对象的方式创建函数 //用的很少,太麻烦了 var sumTwo = new Function(‘num1’, ‘num2’, ‘return num1 + num2’); console.log(sumTwo(10, 30));//40

var fun1 = new Function(“console.log(‘我是fun1’)”); fun1();//我是fun1

//3、(属于函数表达式)用匿名函数-用括号代表一个函数对象 (fucntion(){} …. )

//4、(属于函数表达式)用匿名函数-赋值表达式创建一个函数对象 var fun2 = function(){ //匿名函数 console.log(“我是fun2”); }; fun2();//我是fun2

//5、(属于函数表达式)用匿名函数-在用字面量表达的对象内,创建方法 var obj = { func1:function(){ …. }, }

//6、用原型创建 var obj1 = Object.create(原型);

//7、箭头函数

  1. <a name="kCycv"></a>
  2. #### 匿名函数
  3. - 定义:没有名字的函数
  4. - 调用:直接在匿名函数后面加上小括号
  5. - 函数名本身代表的就是函数体,所以这种调用方式和 func()等效
  6. ```javascript
  7. (function (){
  8. console.log("你好");//你好
  9. })();
  10. (function (a){
  11. console.log(a);//10
  12. }(10));
  13. //注意立即执行函数可以有名字
  14. (function sayHi(){
  15. console.log("你好");//你好
  16. })();
  • 应用 ```javascript //1、用赋值表达式创建一个方法 var func1 = function(){ };

//2、在字面量表达的对象里创建方法 var action = { func2 : function(){ console.log(“我是在json里创建的函数”); } }

//3、做回调函数:由我们创建,但没有直接调用,而函数会在某个时候或者某种条件执行的函数。 //(定时器函数是一个典型的回调函数) setInterval(function(){ console.log(“我是回调函数”); },1000);

//4、事件 var sub = document.querySelector(“#sub”); sub.onclick = function(){ console.log(“按钮被点了”); }

//5、作为函数的返回值 (可能是闭包) function bigFunc(){ console.log(“我是bigFunc”); return function(){ console.log(“我是被作为返回值的函数”); } } console.log(bigFunc);//打印所有代码 console.log(bigFunc());//最终是要打印return值,但是会先执行bigFunc,再打印return console.log(bigFunc()());//最终是要打印return值,但会先执行bigFunc->执行smallFunc->打印return

bigFunc()();//会先执行bigFunc->执行smallFunc

//或者,也可以这么调用👇 var box = bigFunc();//会执行bigFunc box();//执行smallFunc

//6、使用立即执行的匿名函数作为块级作用域 (可能是闭包) function bigFunc(){ (funciton(){ var smallStr = “我是smallFunc”; })(); console.log(smallStr);//报错 }

  1. <a name="c3lAX"></a>
  2. #### 回调函数
  3. - 回调函数可以作为一个参数在另一个函数中被调用
  4. - 实例:
  5. ```javascript
  6. function peaceAndLove(){
  7. console.log("和好了");
  8. }
  9. function quarrel(){
  10. console.log("吵架了");
  11. setTimeout(peaceAndLove, 1000);
  12. }
  13. quarrel();

函数和变量的预解析

变量的预解析

  • 在js中,使用var关键字定义的变量会声明提前,但是不会提前赋值
  • 实质是
    • 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。(所以说,也要当前作用域开始作用的时候才会被声明)
    • 如果作用域是全局,解析器会把var关键字声明的变量加到windows对象的属性里面;如果作用域是函数,解析器会把var关键字声明的变量加到this对象的属性里面。
  • 特点:在其作用域之上只有声明,到该语句,才有赋值。
    1. console.log(a);//exer-1.html:14 Uncaught ReferenceError: a is not defined
    1. console.log(a);//undefined
    2. var a = 10;(加到了全局的windows对象的属性里面)
    ```javascript function hello(){ console.log(a);//undefined var a = 11;//(加到了函数的windows对象的属性里面) } hello();
  1. **函数的预解析**
  2. - js中,使用 **函数声明 **的方法定义函数会声明提前。
  3. - 实质是:
  4. - 函数的声明(那一坨)会被提升到当前作用域的最上面,但是不会调用函数。
  5. - 如果作用域是全局,解析器会把该函数加到windows对象的方法里面;如果作用域是函数,解析器会把该函数加到this对象的方法里面。
  6. - 特点:在其作用域的任何地方都可以无差别调用它。
  7. ```javascript
  8. hello(); //exer-1.html:14 Uncaught ReferenceError: hello is not defined
  1. hello();//hello11
  2. function hello(){
  3. console.log("hello11");
  4. }

arguments

  • arguments 是一个对应于传递给函数的参数的类数组对象。
  • 除箭头函数以外的函数都可以调用这个局部变量。
  • arguments 是一个对象,不是一个 Array 。它类似于Array ,但除了length属性和索引元素之外没有任何Array 属性。

  • 使用

    1. function func(a) {
    2. arguments[0] = 99; // 更新了arguments[0] 同样更新了a
    3. console.log(a);
    4. }
    5. func(10); // 99
  • 把arguments转换为array ```javascript // 由于arguments不是 Array,所以无法使用 Array 的方法,所以通过这种方法转换为数组 var args = [].slice.call(arguments); // 方式一 var args = Array.prototype.slice.call(arguments); // 方式二

// 下面是 es6 提供的语法 let args = Array.from(arguments) // 方式一 let args = […arguments]; // 方式二

  1. <a name="HgwlQ"></a>
  2. #### js自带函数
  3. 所有的函数都要通过一个对象去调用,这也体现了js中的...一切皆对象<br />**宿主对象的方法**
  4. 1. 向页面内输出内容:
  5. `document.write("aaa");`
  6. 2. 打印自己定义的警告信息
  7. `console.warn('自己打印的警告信息');`
  8. 3. 打印自己定义的错误信息
  9. `console.error('自己打印的错误信息');`
  10. 4. 查看数据类型
  11. `console.log(typeof str);`<br />**内建对象的方法**
  12. 1. arr.forEach
  13. 1. 作用:遍历数组
  14. 1. 参数:一个回调函数,function(val,index,arr){},可以在函数里随意使用。
  15. 1. 返回值:没有返回值
  16. ```javascript
  17. var arr = ["周一", "周二", "周三"];
  18. arr.forEach(function(val,index,arr){
  19. console.log("我是第" + index +"个");
  20. console.log("我是" + val);
  21. })
  1. arr.sort

    1. 作用:对数组进行排序
    2. 参数:默认升序,也可以自定义一个回调函数
    3. 返回值:会返回一个排序好的数组,不过sort本身就会改变原数组
      1. var arr = [4, 2, 3, 11];
      2. arr.sort(function(a,b){
      3. //升序:(默认的排序并非标准升序,由于是unicode一位一位的排序,11会排在最前面)
      4. //return a - b;
      5. //降序:
      6. return b - a;
      7. })
  2. arr.splice ```javascript //0、当以该函数为属性值时: var obj = { splice : Array.prototype.splice }

/*1、删除:arr.splice(index, num); 作用:删除 参数:从index号位起删除num个数字。 返回值:被删除的部分以数组形式返回。(删除会直接作用在原数组上)

*/ var arr = [1, 2, 3 ,4]; var brr = arr.splice(index, num); console.log(arr); //[3, 4] console.log(brr); //[1, 2]

/2、添加:arr.splice(index, 0, values); 作用:添加 参数:从index号位起添加任意数量的项 返回值:空数组(添加会直接作用在原数组上) / var arr = [1, 4, 5]; var brr = arr.splice(1, 0, 2, 3); console.log(arr);//[1, 2, 3, 4, 5] console.log(brr);//[]

/3、替换:arr.splice(index, num, values); 作用:替换 参数:从index号位起先删除num项,再添加任意数量的项 返回值:被删除的部分以数组形式返回 / var arr = [1, 4, 5]; var brr = arr.splice(1, 2, 2, 3); console.log(arr);//[1, 2, 3] console.log(brr);//[4, 5]

  1. 4. arr.forEach
  2. 1. 作用:遍历数组
  3. 1. 参数:一个回调函数,function(val,index,arr){},可以在函数里随意使用。
  4. 1. 返回值:没有返回值
  5. ```javascript
  6. var arr = ["周一", "周二", "周三"];
  7. arr.forEach(function(val,index,arr){
  8. console.log("我是第" + index +"个");
  9. console.log("我是" + val);
  10. })
  1. arr.sort

    1. 作用:对数组进行排序
    2. 参数:默认升序,也可以自定义一个回调函数
    3. 返回值:会返回一个排序好的数组,不过sort本身就会改变原数组
      1. var arr = [4, 2, 3, 11];
      2. arr.sort(function(a,b){
      3. //升序:(默认的排序并非标准升序,由于是unicode一位一位的排序,11会排在最前面)
      4. //return a - b;
      5. //降序:
      6. return b - a;
      7. })
  2. Number.toFiexed(3)

    1. 作用:四舍五入到几位小数
    2. 参数:四舍五入到几位
    3. 返回值:新的数
      1. var num = 1.1314;
      2. num = num.toFixed(3);
  3. obj.hasOwnProperty(prop)

    1. 作用:检查该对象是否拥有这个属性或方法
    2. 参数:需要判断的属性名、方法名
    3. 返回值:true / false
      1. obj1.hasOwnProperty(a);
  4. Object.keys(obj)

    1. 作用:获得不包括继承属性的属性名和方法名
    2. 参数:对象
    3. 返回值:以数组的形式返回对应属性名、方法名
      1. var arr = Object.keys(chicken);
  5. 保留

  6. 保留
  7. 保留
  8. 保留
  9. 保留
  10. 保留
  11. 保留
  12. 保留
  13. 保留
  14. 保留

    array


创建方法

  1. //1、用字面量的方式来创建数组
  2. var arr3= [];
  3. var arr1 = [89, 99, 79];
  4. var arr2 = [
  5. {name:'peter', score:99},
  6. {name:'tom', score:99},
  7. {name:'tiger', score:99},
  8. ]
  9. var arr3 = [[1, 2, 3], [4, 5, 6]];
  10. arr3[2] = [7, 8, 9];
  11. console.log(arr3[0][2]);
  12. //2、用array构造函数来创建数组
  13. var arr4 = new Array();
  14. var arr5 = new Array(6);//指定长度
  15. var arr6 = new Array(1, 2, 3);//指定元素
  16. var arr7 = new Array[1];//使用中括号时,无论数字个数,都是指定元素

添加、删除元素

  1. var arr4 = new Array();
  2. arr4[1] = "123";//添加元素的方式,简单粗暴
  3. console.log(arr3[0]);//undefined
  4. //向数组的最后一个位置添加元素
  5. arr4[arr4.length]=13;
  6. //多余的部分会被删除,不过一般不删
  7. arr4.length = 1;

索引

  • 数组的索引是从0开始的
  • 索引:值,类似于:属性名:属性值
  • 数组只能用索引进行访问
  • 访问的数组没有该索引位置 / 该索引没有定义值,会返回undefined

    类数组:arguments

    本质

  • 对象

    1. (function a(){
    2. console.log(arguments);
    3. }(1, 2, 3));

    成立的条件

  • 数组内容的属性名要为索引(数字)

  • 必须要有length属性
  • 最好加上push
    1. var obj = {
    2. 0:'a',
    3. 1:'b',
    4. 2:'c',
    5. length: 3,
    6. push:Array.prototype.push,
    7. //加上splice,就和数组形式一样,可以像数组一样进行调用
    8. splice:Array.prototype.splice
    9. }
    10. obj.push('d');
    运行结果:
    image.png

    数据类型的存储


基本数据类型

  • 栈空间存储
  • 进行比较时,比较值(实质是比较栈空间中的值)

引用数据类型

  • 堆空间(对象名还是存在栈空间的)
  • 进行比较时,比较地址(实质是比较栈空间中的值)

js(javascript)学习记录 - 图7

js类型转换

js强制类型转换

js(javascript)学习记录 - 图8

js隐式类型转换

二元运算符的隐式类型转换

  • 在算术运算中,其它类型的变量都会(隐式)转换成number类型的变量。
  • 除了加号运算符中,如果存在String类型的变量,加号会变成连接符。

    1. var str = "abc";
    2. var num1 = 123;
    3. var str2 = str + num1;
    4. console.log(str2);
    5. console.log(typeof str2);//--->string
  • 做其它运算时可能会得到结果NaN:not a number

    1. NaN也是number类型的
  • 因为隐式转化无法转成number的时候就会转成NaN,而任何数字和NaN做运算都会得到NaN ```javascript var str = “abc”; var num1 = 123; var num2 = str - num1; //var num2 = str * num1; //var num2 = str / num1; //var num2 = str % num1; console.log(num2);//——>NaN console.log(typeof num2);//——>number

var str = “123”; var num1 = 123; var num2 = str - num1; console.log(num2);//—->0 console.log(typeof num2); //——>number

  1. - null会转化成0(truefalse分别是10)
  2. ```javascript
  3. var num1 = 123;
  4. var num2 = null;
  5. var num3 = num1 * num2;
  6. console.log(num3);//0
  7. console.log(typeof num3);//number

一元运算符的隐式类型转换

  • 对于一元运算符,‘+’、‘-’可以使别的数据类型转换为number ```javascript var num = +’12’; console.log(num);//12 console.log(typeof num);//number

var num1 = +’1’ + 2; console.log(num1);//3 console.log(typeof num1);//number

var num2 = -‘1’ + 2; console.log(num2);//1 console.log(typeof num2);//number

  1. <a name="IXKBn"></a>
  2. # js运算符
  3. <a name="dRgeg"></a>
  4. ## 比较运算符-相等运算符
  5. ---
  6. - 相等运算
  7. - 如果类型不同,会先将类型转换成一样再比较,比如 false==0就成立
  8. - string和number做比较,会转换成number
  9. - <br />
  10. - 全等运算(===(全等)和!==)
  11. - 类型和值都一样才成立,比如 false===0就不成立
  12. <a name="aXiLY"></a>
  13. ## 字符串的比较
  14. ---
  15. - 字符串在比较的时候,是一位一位的比较字符的unicode编码,比如str1='abc'和str2='adc'比较,先比较两个'a'的unicode编码,发现一样,然后再比较第二位,以此类推
  16. <a name="Ayxv5"></a>
  17. ## js运算符的优先级
  18. ---
  19. 拿捏不清楚就用括号
  20. | **优先级** | **运算符** | **说明** | **结合性** |
  21. | --- | --- | --- | --- |
  22. | 1 | []、.、() | 字段访问、数组索引、函数调用和表达式分组 | 从左向右 |
  23. | 2 | ++ -- -~!delete new typeof void | 一元运算符、返回数据类型、对象创建、未定<br />义的值 | 从右向左 |
  24. | 3 | *、/、% | 相乘、相除、求余数 | 从左向右 |
  25. | 4 | +、- | 相加、相减、字符串串联 | 从左向右 |
  26. | 5 | <<、>>、>>> | 左位移、右位移、无符号右移 | 从左向右 |
  27. | 6 | <、<=、>、>=、instanceof | 小于、小于或等于、大于、大于或等于、是否<br />为特定类的实例 | 从左向右 |
  28. | 7 | ==、!=、===、!== | 相等、不相等、全等,不全等 | 从左向右 |
  29. | 8 | & | 按位“与” | 从左向右 |
  30. | 9 | ^ | 按位“异或” | 从左向右 |
  31. | 10 | &#124; | 按位“或” | 从左向右 |
  32. | 11 | && | 短路与(逻辑“与”) | 从左向右 |
  33. | 12 | &#124;&#124; | 短路或(逻辑“或”) | 从左向右 |
  34. | 13 | ?: | 条件运算符 | 从右向左 |
  35. | 14 | =、+=、-=、*=、/=、%=、&=、&#124;=、^=、<、<=、>、>=、>>= | 混合赋值运算符 | 从右向左 |
  36. | 15 | , | 多个计算 | 按优先级计算,然后从右向左 |
  37. <a name="vyZ7h"></a>
  38. # 选择结构
  39. <a name="nLWHb"></a>
  40. ## switch选择结构
  41. ```javascript
  42. var month = 3;
  43. switch(month){
  44. case 1:
  45. console.log("1月");
  46. break;
  47. case 2:
  48. console.log("2月");
  49. break;
  50. default:
  51. console.log("咩有定义这种情况");
  52. break;
  53. }

异步

实现方式

使用可以调用回调函数的函数

  • setTimeout ```javascript //简单一点 var func = function(callback){ setTimeout(function(){
    1. callback();
    },1000) } var a = function(){ console.log(“我是回调函数”); } func(a);

//复杂一点 function async_fun(param, callback){//把function传进来准备调用 setTimeout(()=>{ callback(‘异步操作:’ + param);//这里才真的调用 }, 1000); }

async_fun(1, function(a){ console.log(a); async_fun(2, function(a){ console.log(a); async_fun(3, function(a){ console.log(a); }) }) })

  1. <a name="dHKR7"></a>
  2. ### 事件监听
  3. - 传统事件监听
  4. ```javascript
  5. //注意:对于传统事件监听,当同一个对象绑定多个相同事件时,只有最后一次生效
  6. //传播模式只能是冒泡,即由里到外
  7. window.onload=function(){
  8. alert("页面加载完毕");
  9. }
  10. document.getElementById("btn").onclick=function(){
  11. alert("按钮被点击");
  12. }
  13. document.onmousemove=function(){
  14. console.log("鼠标在移动");
  15. }
  • addEventListener() ```javascript //注意:对于addEventListener,可以给同一个对象绑定多个相同事件,按代码顺序执行 //一般不用,因为兼容性不好, Internet Explorer 8 及更早IE版本不支持 addEventListener() 方法,Opera 7.0 及 Opera 更早版本也不支持。 window.addEventListener(‘load’,init,false); function init(){ alert(“页面加载成功”); } //第三个参数是传播模式——> false:又里到外冒泡 true:由外到里捕获

// 下面写法与上面等价 window.addEventListener(‘load’,function(){ alert(“页面加载成功”); },false);

  1. - ajax
  2. - ajax是什么?
  3. - ajax是一种不刷新页面同时更新页面的一种技术。
  4. - ajax怎么使用?
  5. - XMLHttpRequest构造函数
  6. - xhr.open(请求方法,URL,是否异步请求)
  7. - 请求方法:'get''post'
  8. - xhr.onreadystatechange = () => {}
  9. - xhr.readyState(响应状态码)
  10. - 0:未调用open方法;
  11. - 1:调用了open但没有调用send;
  12. - 2:发送请求但没有收到响应;
  13. - 3:收到了部分响应,4表示响应都接收完了。
  14. - xhr.status(状态码)
  15. - 200:请求被正常处
  16. - 404:服务器无法找到对应资源
  17. - 400:请求无效
  18. - 401:请求需要认证
  19. - 500:服务器内部错误
  20. - 301:永久重定向
  21. - 302:临时重定向
  22. - 101:切换协议
  23. - xhr.responseText
  24. - xhr.send(null)
  25. ```javascript
  26. let xhr = new XMLHttpRequest();
  27. xhr.open('get','/eloise/visitRecord',true);
  28. xhr.onreadystatechange = () => {
  29. if(xhr.readyState === 4){
  30. if(xhr.status === 200){
  31. console.log(xhr.responseText);
  32. console.log(JSON.parse(xhr.responseText));
  33. }
  34. }
  35. };
  36. xhr.send(null);

Promise

本质

  • 构造函数
    • 该构造函数有一个参数,这个参数是回调函数。这个回调函数有两个参数,分别是resolve和reject,它们俩也都是回调函数。
      • then
        • 作用
          • 可以接收到resolve和reject传递过来的数据
          • then方法里面有两个参数,这两个参数都是回调函数。(一般都设置为箭头函数)
      • resolve
        • 作用
          • 设置异步操作执行成功之后怎么做
          • 向外面传递异步操作执行成功之后的数据
      • reject
        • 作用
          • 设置异步操作执行成功之后怎么做
          • 向外面传递异步操作执行失败之后的数据
      • catch
        • 作用
          • 同reject,两者二选一使用即可。
  • 状态机,也就是通过设置不同的状态,来告诉用户异步操作是执行成功了还是执行失败了。

    • 状态
      • 初始状态:pending
      • 成功状态:resolved / fulfilled (resolve回调函数就是将Promise的状态从pending设置为resolved)
      • 失败状态:rejected

        好处

  • 在异步执行的流程中,清晰的分离执行代码和处理结果。

    代码

  • 基本版 ```javascript function test(resolve, reject){ var dp = true; if(dp === true){

    1. resolve("dp是true");

    }else{

    1. reject("dp是false")

    } } function dispose(){ return new Promise(test).then(res=>{

    1. console.log(res);

    }).catch(err => {

    1. console.log(err);

    }) }

//被注释掉的dispose和上面那种写法等效 //function dispose(){ // return new Promise(test).then(res=>{ // console.log(res); // }, err=>{ // console.log(err); // }) // }

dispose();

  1. - 不要test的改写版
  2. ```javascript
  3. function dispose(){
  4. return new Promise(function(resolve, reject){
  5. var dp = true;
  6. if(dp === true){
  7. resolve("dp是true");
  8. }else{
  9. reject("dp是false")
  10. }
  11. }).then(res=>{
  12. console.log(res);
  13. }, err=>{
  14. console.log(err);
  15. })
  16. }
  17. dispose();
  • 标准版
    • 可以做到更加清晰的分离 ```javascript function dispose(){ return new Promise(function(resolve, reject){ var dp = true; if(dp === true){ resolve(“dp是true”); }else{ reject(“dp是false”) } }) }

dispose().then(res=>{ console.log(res); }, err=>{ console.log(err); });

  1. - 链式then
  2. - 原理:then本身就会返回一个promise对象,但如果不写成return new Promise的形式就只有resolve,没有reject
  3. ```javascript
  4. function dispose(){
  5. return new Promise(function(resolve, reject){
  6. var dp = true;
  7. if(dp === true){
  8. resolve("dp是true");
  9. }else{
  10. reject("dp是false")
  11. }
  12. })
  13. }
  14. dispose().then(res=>{
  15. console.log(res);
  16. var dp2 = true;
  17. return new Promise(function(resolve, reject){
  18. if(dp2 === true){
  19. resolve("dp2是true");
  20. }else{
  21. reject("dp2是false")
  22. }
  23. })
  24. }, err=>{
  25. console.log(err);
  26. }).then(res => {
  27. console.log(res);//我是链式then中的第二个then
  28. });
  • 并行执行异步任务

    1. let p1 = new Promise((resolve, reject) =>{
    2. resolve("成功了");
    3. })
    4. let p2 = new Promise((resolve, reject) =>{
    5. resolve("success");
    6. })
    7. Promise.all([p1, p2]).then(res=>{
    8. console.log("都做了");
    9. console.log(res);
    10. })

    应用

  • 超时处理

  • 封装处理ajax请求

    • 原生封装

      1. function ajax(){
      2. return new Promise(function(resolve, reject){
      3. let xhr = new XMLHttpRequest();
      4. xhr.open('get', '/eloise/visitRecord', true);
      5. xhr.onreadystatechange = () =>{
      6. if(xhr.readyState === 4){
      7. if(xhr.status === 200){
      8. console.log("here");
      9. resolve(JSON.parse(xhr.responseText));
      10. }else{
      11. reject(xhr.status);
      12. }
      13. }
      14. }
      15. xhr.send(null);
      16. })
      17. }
      18. ajax().then(res =>{
      19. console.log(res);
      20. }).catch(err =>{
      21. console.log("失败原因:"+err);
      22. })
    • axios

      • 本质:一种基于Promise封装ajax的技术。 ```javascript //在react中引入axios import axios from ‘axios’; React.Component.prototype.$axios = axios;

this.$axios.get(‘/eloise/visitRecord’).then(res=>{ console.log(res.data); }).catch(function (error) { console.log(error); });

  1. - fetch
  2. - 本质:一个基于promise封装的一个http库,是原生js,没有封装ajax,也没有xhr
  3. - 特点
  4. - 原生函数, 但老版本浏览器不支持,为了兼容低版本的浏览器, 可以引入兼容库fetch.js
  5. - fetch对应的后端只能是get
  6. - react里,使用fetch不需要引入什么,直接fetch就用了
  7. <a name="pEEy1"></a>
  8. #### promise的好处和优点
  9. - promise 的出现解决了传统 callback 函数回调地狱的问题,支持链式调用,但当遇到复杂的业务逻辑时,过多的调用then会显得很不美观。
  10. - 回调地狱是什么?
  11. - .因为javascript是单线程的,所以有些需要等待的地方,需要使用回调函数。但如果业务复杂,就需要写多层的回调嵌套,这样的代码不仅书写麻烦、可读性差,而且难以维护,这就称作回调地狱。一般处理的方式是使用promise或者async函数。
  12. <a name="qBLxq"></a>
  13. ### async/await
  14. <a name="uQkYW"></a>
  15. #### 使用
  16. <a name="DI2GG"></a>
  17. #### 和promise的区别
  18. - async,await 是基于 promise 实现的,async函数会返回一个Promise对象。它是基于 Generator 函数的语法糖,拥有内置执行器,可以使异步代码看起来像同步代码一样,不需要再写promise里的then,更加方便阅读和理解。
  19. <a name="bezMW"></a>
  20. #### 应用
  21. - 单独使用
  22. - 普通函数
  23. ```javascript
  24. //便于理解(就写在全局作用域的,咋没看出来非法)
  25. async function a(){
  26. await setTimeout(b,1000);
  27. await c();
  28. await d();
  29. }
  30. function b(){
  31. console.log("b函数");
  32. }
  33. function c(){
  34. console.log("c函数");
  35. }
  36. function d(){
  37. console.log("d函数");
  38. }
  39. a();

image.png

  1. //实际应用:这是react里的一个函数
  2. componentDidMount() {
  3. var that = this;
  4. async function getData(){
  5. const url1 = '/eloise/visitRecord';
  6. const url2 = '/eloise/indexChoice';
  7. const res1 = await that.$axios.get(url1);
  8. console.log(res1.data);
  9. const res2 = await that.$axios.get(url2);
  10. console.log(res2.data);
  11. }
  12. }
  • 箭头函数 ```javascript var a = async () =>{ await b(); await c(); }

function b(){ console.log(“b函数”); }

function c(){ console.log(“c函数”); }

a();

  1. - 立即执行函数
  2. ```javascript
  3. (async ()=>{
  4. await b();
  5. await c();
  6. }
  7. )()
  8. function b(){
  9. console.log("b函数");
  10. }
  11. function c(){
  12. console.log("c函数");
  13. }
  14. a();
  • 用在promise链中

    • 条件:async函数需要返回一个Promise对象,可以直接return await,因为await会返回一个promise对象

      1. componentDidMount() {
      2. var that = this;
      3. async function getData(){
      4. const url1 = '/eloise/visitRecord';
      5. const url2 = '/eloise/indexChoice';
      6. const res1 = await that.$axios.get(url1);
      7. console.log(res1.data);
      8. return await that.$axios.get(url2);
      9. }
      10. getData().then(res=>{
      11. console.log(res.data);
      12. },err=>{
      13. console.log(err);
      14. })
      15. }

      处理错误

  • 通过promise中then方法的回调函数参数来处理

    1. //url2是一个错误地址
    2. componentDidMount() {
    3. var that = this;
    4. async function getData(){
    5. const url1 = '/eloise/visitRecord';
    6. const url2 = '/eloise/indexChoice1';
    7. const res1 = await that.$axios.get(url1);
    8. console.log(res1.data);
    9. return await that.$axios.get(url2);
    10. }
    11. getData().then(res=>{
    12. console.log(res.data);
    13. },err=>{
    14. console.log(err);
    15. })
    16. }
  • 判断状态码,直接抛出错误

    1. //虽然url2是对的,但是then方法还是进了error那里
    2. componentDidMount() {
    3. var that = this;
    4. async function getData(){
    5. const url1 = '/eloise/visitRecord1';
    6. const url2 = '/eloise/indexChoice';
    7. const res1 = await that.$axios.get(url1);
    8. if(res1.status !== 200){
    9. throw new Error(res1.status)
    10. }
    11. return await that.$axios.get(url2);
    12. }
    13. getData().then(res=>{
    14. console.log(res.data);
    15. },err=>{
    16. console.log("我是err");
    17. console.log(err);
    18. }).catch(cat =>{
    19. console.log("我是catch");
    20. console.log(cat);
    21. })
    22. }
  • 通过try..catch..

    闭包

    闭包的基本点

  • 定义:定义在一个函数内部的、且 调用了 该函数 局部变量 的函数。

  • 闭包产生的条件
    1. 函数嵌套
    2. 子函数要使用函数内部的变量
  • 性质:
    • 闭包是连接函数内外部的桥梁。可以实现在函数外部操作函数内部。
      • 所以只要不return出去,外面只能干瞪眼
    • 闭包对应的函数中的局部变量是常驻内存的,以此来实现缓存。
      • 因此,往往需要保证外部函数只执行一次。否则对象重新创建、重新初始化,无法实现缓存(实质是无法实现变量常驻内存)。
        • 可以使用立即执行函数,保证只执行一次。
        • 只要保证之后操作的一直是return值(闭包函数)就没问题。
        • 外部函数只执行一次,是实现缓存的基础。缓存又是实现其它应用的基础。
  • 缺点
    • 由于对应变量常驻内存,可能导致内存泄漏
      • 如何解决:将外部调用闭包的对象置为null ```javascript function f1(){ var a = 1; function f2(){ console.log(++a);//函数内部 + 调用局部变量—->满足闭包定义 } return f2;//return出去,实现桥梁 }

var f3 = f1();//f1只执行一次,实现缓存 f3();//2 f3();//3

fn3 = null;//闭包fn2被回收,避免内存泄漏

  1. <a name="oYBsw"></a>
  2. ## 闭包的应用
  3. 1. 实现缓存
  4. 1. 规则:
  5. 1. 缓存里如果没有的话,就新建对象到缓存,如果缓存里面有,就直接从缓存里面拿。
  6. 1. 外部函数只能执行一次。//所以这里是立即执行
  7. 2. 好处:节约资源,不必反复创建同一对象。
  8. ```javascript
  9. var Cache1 = (function(){
  10. var cache = {};//变量cache会常驻内存
  11. return {//也可以先定义一个有名字的局部方法,再return出来
  12. getObj:function(name){
  13. if(name in cache){
  14. return cache[name];
  15. }
  16. var temp = new Object(name);
  17. cache[name] = temp;
  18. return temp;
  19. }
  20. }
  21. })();
  22. console.log(Cache1);//return对象
  23. console.log(Cache1.getObj('乔碧萝'));
  1. 实现封装过程,私有化变量

    1. 规则
      1. 封装对象中的变量不能直接访问,只能通过闭包来访问。
    2. 好处:避免非法访问
      1. var person = (function(){
      2. var name = "还没有名字";//外部访问不到name值,打印person.name是undefined,但可以通过函数看到
      3. return{
      4. getName:function(){
      5. return name;
      6. },
      7. setName:function(value){
      8. name = value;
      9. }
      10. }
      11. }());
      12. person.setName("小张");
      13. console.log(person.getName());//小张
      14. //如果person执行两次,那么setName就不会生效
  2. 自定义模块

    1. 规则
    2. 好处:每个模块都可以定义相同名字的变量,多模块协作时也不会引起冲突。
    3. 本质:可以使得同名属性最终加载到不同的对象上
      1. //exer-1.js
      2. function module1(){
      3. var m = "我是模块1中定义的变量m";
      4. function a(){
      5. return m + ":a函数";
      6. };
      7. function b(){
      8. return m + ":b函数";
      9. };
      10. return{
      11. a:a,
      12. b:b
      13. }
      14. }
      ```javascript //exer-2.js (function(){ var m = “我是模块2中定义的变量m”; function a(){ return m + “:a函数”; }; function b(){ return m + “:b函数”; }; window.module2 = { a:a, b:b } })();

//标准来讲,应该这么写: //因为js里面涉及压缩的问题,window可能会被压成w,上面的写法就不行了 (function(window){ var m = “我是模块2中定义的变量m”; function a(){ return m + “:a函数”; }; function b(){ return m + “:b函数”; }; window.module2 = { a:a, b:b } })(window);

  1. ```javascript
  2. //1.html
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6. <meta charset="UTF-8">
  7. <title>js简单实例</title>
  8. </head>
  9. <body>
  10. </body>
  11. <script src="exer-1.js"></script>
  12. <script src="exer-2.js"></script>
  13. <script>
  14. //模块1
  15. var accept = module1();
  16. console.log(accept.a());
  17. //模块2
  18. console.log(module2.a());
  19. </script>
  20. </html>

this


  • this是什么?
    • this代表的是函数(方法)所在的那个对象。【this是一个指示代词,是这、这个的意思,
    • this就是代码执行时当前的context object
  • this的指向问题

    • 纯粹的函数

      • this指向window
        1. function test(){
        2. console.log(this)
        3. }
        4. test(); //window
    • 对象中的方法

      • this指这个上级对象 ```javascript //形式1 function test(){ console.log(this); } var obj = {}; obj.fn = test; obj.fn();//obj

//形式2 var obj = { fn:function(){ console.log(this); } } obj.fn();//obj

  1. - 对象方法中的方法
  2. - 指向window
  3. ```javascript
  4. var obj = {
  5. f1:function(){
  6. var f2 = function(){
  7. console.log(this);//window
  8. }
  9. f2();
  10. }
  11. }
  12. obj.f1();
  • 作为构造函数调用
    • this指向新对象 ```javascript function Test(){ console.log(this); } var obj = new Test();//Test()

Test.prototype.sayHello = function(){ console.log(this); } obj.sayHello();//Test()

  1. - 箭头函数
  2. - 箭头函数内部所有的this指向首层父级作用域的this
  3. - 自执行函数
  4. - 指向window
  5. ```javascript
  6. var obj = {
  7. f1:function(){
  8. console.log(this);
  9. },
  10. f2:(function (){
  11. console.log(this);//window
  12. })()
  13. }
  14. obj.f1();//obj

image.png

  • 回调函数
    • 被箭头函数包裹
      • 首层的父级作用域
    • 不被箭头函数包裹
      • window
        • 为什么在函数里可以直接打印this?
  • 因为浏览器(解析器)在调用函数的时候,会向函数的内部传递一个隐含的参数,这个参数就是this

    1. function fun1(){
    2. console.log(this);
    3. }
    4. fun1();//window
    5. window.fun1();//window
    6. var zhubajie = {
    7. name:"猪八戒",
    8. f:fun1
    9. }
    10. zhubajie.f();//object->zhubajie
  • this的作用
    • 替代对象
      1. var zhubajie = {
      2. name:"猪八戒",
      3. age:3,
      4. introduction:function(){
      5. console.log("我是" + this.name);
      6. console.log("我今年" + zhubajie.age + "岁了"); //这样也可以,但是万一要改对象的名字,多了 //就不好改了
      7. }
      8. }

      原型


原型的基本点

什么是原型

  • 原型是function对象的一个object属性。
    • 默认的 “prototype” 是只有一个属性 constructor,属性 constructor 指向构造函数自身。
    • prototype.constructor也可以改变
  • 它定义了以该函数作构造函数时,创建的对象的公共组先,这些对象直接继承原型的属性和方法。 ```javascript //1、为构造函数的属性prototype赋值 Person.prototype = { skeleton:206, chara:”lazy” } //或者:Person.prototype = new DaXingXing(); function Person(){

} var p1 = new Person(); var p2 = new Person();

//2、法二:使用proto var animal = { eat:”动物都会吃饭”, };

var chicken = { crow:”鸡会打鸣”, proto:animal }; //或者 chicken.proto = animal

  1. <a name="FvGDl"></a>
  2. ### 原型的作用
  3. - 数据共享,节省空间
  4. - 继承:增加了复用性、可读性、维护性
  5. <a name="uKPec"></a>
  6. ### 原型的特点
  7. - 原型只用于读取,不用于写入、删除。
  8. - 引用值除外,可以给引用值加属性
  9. - 写入非引用值外的属性,相当于给自己加属性
  10. - 先在非继承属性和方法中找,找不到再向原型链找。
  11. - 查看A对象的原型链上有没有B的原型(b.prototype)
  12. ```javascript
  13. A instanceof B //返回true or false

image.png

  1. var animal = {
  2. eat:"动物都会吃饭",
  3. sleep:function(){
  4. console.log("动物都会睡觉");
  5. }
  6. };
  7. var chicken = {
  8. crow:"鸡会打鸣",
  9. __proto__:animal
  10. };
  11. chicken.sleep();//从原型读取:动物都会睡觉
  12. chicken.sleep = function(){ //直接在对象上写入
  13. console.log("鸡会站着睡觉");
  14. }
  15. chicken.sleep();//鸡会站着睡觉
  16. delete chicken.sleep; //直接在对象上删除
  17. chicken.sleep();//动物都会睡觉

原型的限制

  • 引用不能形成闭环。如果我们试图在一个闭环中分配 proto,JavaScript 会抛出错误。//?没错,会报undefined
  • proto 的值可以是对象,也可以是 null。而其他的类型都会被忽略(访问显示undefined)

    proto是什么?

  • proto是对象所独有的(函数也是对象)

  • p1.proto === Person.prototype
  • proto (Google) 是 [[Prototype]] (ES6) 的 getter/setter
  • proto属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的proto属性所指向的那个对象(父对象)里找,一直找,直到proto属性的终点null,再往上找就相当于在null上取值,会报错。通过proto属性将对象连接起来的这条链路即我们所谓的原型链。
  • 现代编程语言建议我们应该使用ES5引入的函数 Object.getPrototypeOf/Object.setPrototypeOf 来取代 proto 去 get/set 原型。因为有些浏览器并不支持这个proto属性,所以并不是完全兼容的。

    constructor是什么

  • 单从constructor这个属性来讲,只有prototype对象才有

  • 每个对象(函数也是对象)都可以找到其对应的constructor,因为创建对象的前提是需要有constructor,而这个constructor可能是对象自己本身显式定义的或者通过proto在原型链中找到的
  • constructor属性的含义就是指向该对象的构造函数
  • 所有函数(此时看成对象了)最终的构造函数都指向Function

    for…in循环

  • for…in循环会迭代①包括继承的在内的②可枚举的所有属性名和方法名

  • 结合hasOwnProperty可以只循环自己的属性
    • 疑问:hasOwnProperty这个属性是哪里来的?
      • 该方法是 Object.prototype.hasOwnProperty 提供的。换句话说,它是继承的。
    • 疑问:那为什么 hasOwnProperty 没有像 eats 和 jumps 那样出现在 for..in 循环中?
      • 它是不可枚举的。就像 Object的其他属性,hasOwnProperty 有 enumerable:false 标志。
  • 数组也能for…in ```javascript //迭代包括继承在内的属性 for(var i in chicken){ console.log(chicken[i]); }

//结合hasOwnProperty可以只循环自己的属性 for(var i in chicken){ var isOwn = chicken.hasOwnProperty(i); if(isOwn){ console.log(chicken[i]); } }

  1. <a name="VS3ey"></a>
  2. ## 共享原型(的继承)
  3. - 因为是引用,所以原型不能随意改变,改一个会把大家的都改了
  4. ```javascript
  5. function(Target, Origin){
  6. Target.prototype = Origin.prototype;
  7. }

圣杯模式(的继承)

  • 增加一个F中间层
  • 更改Son.prototype不会改变Father.prototype ```javascript fucntion F(){}; F.prototype = Father.prototype; Son.prototype = new F();

function(Target, Origin){ //增加一个中间层 function F(){}; //先定义中间层的原型 F.prototype = Origin.prototype; //再定义Target的原型 Target.prototype = new F();

//考虑constructor要指向自己的构造函数 Target.prototype.constructor = Target; //如果有一天想知道真的继承自谁,可以通过这种方法去调用 Target.prototype.uber = Origin.prototype; }

  1. - 创建原型
  2. ```javascript
  3. //1、自定义原型 法一:使用__proto__
  4. var animal = {
  5. eat:"动物都会吃饭",
  6. };
  7. var chicken = {
  8. crow:"鸡会打鸣",
  9. __proto__:animal
  10. };
  11. //或者 chicken.__proto__ = animal
  12. console.log(chicken.eat);//动物都会吃饭
  13. var egg = {
  14. circle:"鸡蛋是圆的",
  15. __proto__:chicken
  16. }
  17. //1、查看原型
  18. var xiaowang = new Object();
  19. console.log(Object.getPrototypeOf(xiaowang));

call/apply、bind

基本概念

作用

改变this指向

  1. //1、call用法1
  2. test()<--->test.call();
  3. //2、call用法2:如果有参数,第一位得传this的指向
  4. var obj = {}
  5. function Person(name, age){
  6. this.name = name;
  7. this.age = age;
  8. }
  9. Person.call(obj, 'ANNA', 19);
  10. //obj = {name:'ANNA', age:19};
  1. //1、apply用法:
  2. //call需要把实参按照形参的个数传进去
  3. //apply需要传一个arguments
  4. Person.apply(obj, ['ANNA', 19]);
  1. //call和apply返回一个函数调用,但bind只会返回一个函数
  2. //1、bind用法1
  3. Person.bind(obj, 'ANNA', 19)();
  4. //2、bind用法2
  5. Person.bind(obj)('ANNA', 19);

应用

(借用构造函数的)继承

  • 不能继承借用构造函数的原型
  • 每次构造函数都要走一个函数(从运行上没省)

    1. function Person(name, age, sex){
    2. this.name = name;
    3. this.age = age;
    4. this.sex = sex;
    5. }
    6. function Student(name, age, sex, grade){
    7. Person.call(this, name, age, sex);
    8. this.grade = grade;
    9. }
    10. var student = new Student();

    区别是数组还是对象

  • 综合使用Object.prototype.toString方法和call方法 ```javascript Object.prototype.toString.call([]);//[object Array] 第一个单词小写,第二个单词大写 Object.prototype.toString.call({});//[object Object]

//真实应用 var toStr = Object.prototype.toString; if(toStr.call(target) == “[object Array]”){ console.log(“这是数组”); }else{ console.log(“这是对象”); }

  1. <a name="krGia"></a>
  2. # try..catch
  3. <a name="vvxQ3"></a>
  4. ## 作用
  5. - try用于容错,不会执行错误后的try里面的代码,不影响try..catch后面的代码,不会向控制台抛出异常
  6. - catch用于捕获异常,只有在try代码块中出现错误代码时才会执行catch里的代码
  7. ```javascript
  8. try{
  9. console.log('a');
  10. console.log(b);//运行到这里停止
  11. console.log('c');
  12. }catch(e){
  13. console.log(e);//ReferenceError: b is not defined
  14. // at exer-1.html:13
  15. console.log(e.message);//b is not defined
  16. console.log(e.name);//ReferenceError
  17. }
  18. console.log('d');

image.png

错误类型

  1. EvalError:eval()的使用与定义不一致
  2. RangeError():数值越界
  3. ReferenceError:非法或不能识别的引用数值
  4. SynaxError:语法解析错误
  5. TypeError:操作数类型错误
  6. URLError:URL处理函数使用不当

    dom


  • 全称:Document Object Model,即文档对象模型。
  • 概念:DOM 是 W3C(万维网联盟)的标准,是关于如何获取、修改、添加或删除 HTML 元素的标准。