JS基础知识点

JS基础知识点

    • 一、JavaScript的定义
    • 二、var未知量(变量)
    • 三、`const` 常量
    • 四、数据类型
      • 4.1常用的类型有6个
      • 4.2操作符
    • 五、对象
      • 5.1 对象的定义
      • 5.3对象的遍历
    • 六、数组
      • 6.1 数组的定义
      • 6.2 遍历数组
      • 6.3 创建数组
    • 七、函数
      • 7.1函数的定义
      • 7.2形参与实参
      • 7.3作用域
      • 7.4 函数声明表达式
      • 7.5 Callback回调函数
      • 7.6 函数的多种形态
      • 7.8 异步操作
    • 九、深浅拷贝

一、JavaScript的定义

  1. 最初名称livescript,真名ecmascript,目前称作JavaScript

  2. JavaScript是一种脚本语言,是一种解释型、动态类型、弱类型、基于原型的语言,内置支持类型;

    Javascript 的变量没有类型约束(没有类型系统);

    它只能通过web浏览器进行解释和执行才能去完成某种操作,而不能独立运行。

  3. javascript编码规范:ECMAScript

  4. javascript是面向函数式编程,所有的编程都面向于函数

  5. javaScript产生的原因:

    JavaScript出现之前,web浏览器只是一个能够显示超文本文档的软件的基础部分;JavaScript的出现是为了实现用户与客户端的交互;

    用户与客户端的交互:客户端给用户提供功能,用户通过某些操作说明用户的要求,客户端对这些操作做出的反馈;

    • 综上所述:JS为了提高用户体验而产生。
    <body>
    	<script type="text/javascript">
            var x="hellow";		声明	赋值
            console.log(x);
        script>
    body>
    
  6. JavaScript 是非纯函数式的编程语言(的原因)

    1. 函数的声明语句可以作为表达式来使用(其他语言并没有这种操作)
    2. 函数可以打坐值来传递,比如将函数赋值为某个变量或形参
    3. 函数可以作为返回值


二、var未知量(变量)

  1. 变量必须使用 var 关键字定义。

  2. 变量的目的:用于存储东西,赋予的值可以改变。

  3. 变量标识符(变量名):必须由字符、下划线、$符、_符为开头,不可以用数字开头,同时不能使用关键字和保留字;

    • html 不区分大小写,但 Javascript 严格区分;
    • 命名时建议使用小驼峰命名法,构造函数使用大驼峰命名法。
  4. 如何生成变量:声明一个变量,并且赋值;

    创建变量的过程叫声明;

    如何声明:var x; 声明语句

  5. 声明分两种:

    • 显示声明,使用var
    • 隐式声明,对一个未声明的变量直接赋值,就会使它被隐式声明,但是这种变量都是全局变量
  6. 赋值是指将某一数值赋给某个变量的过程;

    赋值语句是指将确定的数值赋给变量的语句。

  7. 语法糖:声明与赋值写在一起

    变量的声明与定义实际上是两个不同的步骤,但我们可以用语法糖完成声明与定义的一次性操作;
    若一个变量只有声明而没有定义,则该变量的值为 undefined

    var s = 9;
    
  8. 声明提前

    变量(包括函数)具有声明提前的特性,把所有变量的声明部分都被放到该变量所在的作用域的顶端。声明提前的过程是在预解析过程中。

    声明提前执行的过程:执行上下文赞,也叫预解析。

    预解析:在代码运行之前,会调整一次代码,但不执行代码,在这个过程中,就会把变量声明部分提前,但不提前赋值部分。

    • 现象:若声明变量在 console 后,则输出值为 undefined
    console.log(any); // undefined
    var any = "hello world!";
    


三、const 常量

  1. 常量使用 const 关键字进行定义

  2. const 实现早于规范,在 es6(es2015)版本当中才被定义为标准,所以依然有很强的兼容性问题,IE11 以下不支持。

  3. 声明的同时必须赋值定义 const s = 9;,即声明与定义是一个整体

  4. 常量的标识符建议使用全部大写形式,必须由字符、下划线、$符、_符为开头,不可以用数字开头

  5. 常量不可重新赋值:常量的特点是值一旦定义不可改变,常量一旦被改变将会报错并终止代码运行;这也是常量的声明与定义必须同时完成的原因。

  6. 常量不具备变量的声明提前的特性

    原因:假设常量可以拆分声明与赋值,那么常量在声明的时候未被赋值,实际上它被赋予了特殊的undefined 值,当我们后续赋值时,就必须要去改变 undefined,这种该行为实际上已经在改变常量的值了,与常量的概念冲突。

    常量所存储的地址指向一旦发生变化将会触发类型错误

    const one = 45;
    	one = 34;			改变了地址(不是引用地址,是指向45的地址),报错
    const two = [];
    	two[0] = 45;		数组地址没有改变
    


四、数据类型

4.1常用的类型有6个

  • 数值型 number

  • 字符串 string

  • 布尔值 booleantruefalse.toString

  • 未定义 undefined,没有被定义的量都默认定义为undefined

    本质是变量,在现阶段浏览器中该值无法更改,不能做任何对象操作(没有任何属性,也没有基本包装转换(无构造函数),不能进行点操作,没有原型对象,null一样)

  • 空指针 null

  • 对象 object,它也是语法糖

注意:在整个 JavaScript 中只有 nullundefined 不能进行点操作

  • 基本数据类型分两种:值类型(直接量)、引用类型

    • 值类型:String、Number、Boolean、null、undefined
    • 引用类型:Object
    • JS 一切皆对象,在 Object 类型的基础上又构建了一些其他的引用类型:
      • Function.prototype
      • Array.prototype
      • Date.prototype
      • Arguments.prototype 是类数组对象(无法构造,自动生成),并不能找到它的构造函数的原型,它的原型非常特殊被系统隐藏,它的构造函数就是 Arguments 而不是对象,它是独立特殊的一类
      • Number、String、Boolean 的基本包装类型
      • HTMLCollection 节点集合,也是一种构造函数
    • 注意:值类型和基本包装类型(对象,引用类型属于工具类型,只在某个过程中有效,是隐式的转换过程)不是同一个类型。
  • JavaScript支持三种范式面向对象、面向过程、函数

  • NaN :当 number 类型与另一个类型一起操作时,无法进行任何类型转换,所以无法拿到结果值,即想要返回一个 number,但是最终拿不到结果


4.2操作符

  1. 操作数与操作符

    操作符,在 JavaScript 中操作表达式并返回结果的符号或者字符

    操作符是对数据进行操作的符号,操作符操作完数据后一定会产生结果

    数值操作符 +、-、*、/

    比较操作符 >、<、>=、<=、=、!=、-> 都是布尔值

    • == 与 === 的区别

      不同的数据类型无法进行值的比较

      双等操作符在进行数据比较时,会进行隐式类型转换(看不见转换过程,向string / number方向转换)

      全等操作符,又称恒等操作符,首先判断类型,只要类型不同就返回false,没有隐式类型转换

    • ()用来决定操作范围

    四则运算符号计算的结果为数值类型
    var num = 88 + 1 ;
    console.log(num);
    >比较操作符,比较运算符计算结果为布尔值
    console.log( 88 > 99);
    
  2. 比较操作符

    • || 或 &&与 !非

    • 单目操作符,又称逻辑操作符

    • 在 JavaScript 中比较操作符或、与返回值是表达式的值,而不是 boolean;非返回布尔值

      function f1(){
          console.log("call f1")
          return 0;
      }
      function f2(){
            console.log("call f2")
         return 1;
      }
      console.log(f1() || f2())	f1返回0,f2返回3,则执行f1和f2,返回值为3
      console.log(f2() || f1())	f2返回3,则执行f2,短路现象,返回值为0
      console.log(f1() && f2())	f1返回0,则执行f1,返回值为0
      console.log(f2() && f1())	f2返回3,f1返回0,则执行f1,f2,返回值为0
      
      特殊情况(当有一个值为0时):
      if(f1() && f2()){			f1返回0,则不执行if内部语句
       	console.log("++++");	
      }
      if(f1() || f2()){			f1返回0,f2返回3,则执行if内部语句
          console.log("++++");
      }
      

      注意:或、与操作符若发生短路,则返回 f1;若没有短路则返回 f2

  3. typeof是一目操作符,for-in 中 in 是二目操作符

    • 有多少个操作数,就是多少目操作符

    • 只能返回基本数据类型

    • 书写形式:

      console.log(typeof 变量标识符);
      
  • JavaScript 提供了 typeof 操作符来得到一个值的所属类型,typeof 会得到以下的结果:
操作 结果
typeof 55 / typeof (34) number
typeof "hello" string
typeof true boolean
typeof undefined undefined
typeof null object
typeof {info: "info"} object
typeof [1, 2, 3, 4] object
typeof function(){} function
typeof arguments Object

以上结果 与 **”函数“**存在特殊性:

  • 的确被判定为一种独立的类型,但是的本质指向堆空间(对象)的一个引用地址,只不过该地址比较特殊,根据该地址没有指向任何数据,也就是常说的空指针。
    • 的本质是一个 ,但是为了突出函数在 JS 中的重要性,因此 最终会返回一个描述来提高重视。函数在 JS 中又被成为**”一等公民“**,虽然函数本质是由对象构建,但是由于它在该语言中非常的重要,因此认为函数与对象是同等地位(与非纯函数式的编程语言相关)。

引用类型:

  • 数组本质上也是对象;数组、、标准统称为引用类型;引用类型属于浅拷贝。

  • 不是函数 console.log ( typeof (34) );

    console.log(typeof (34));中34的括号,不是函数,是为了声明范围改变优先级

  1. 三目运算符 ? :

    express1?express2:express3

    解析:若express1为真(非零),则执行express2,否则执行express3

    用null或undefined,undefined表示该处未定义表达式

    (index > 10)? (index = 0): undefined;
    (index < 10) || (index = 0)	表示若index>10就执行index = 0 赋值表达式
    
  2. i++i = i+1 的区别

       console.log(i = 5)
       i = 5是赋值表达式,console返回的值是表达式的值
       i++ 先取值后加一
       ++i 先加一后取值
       
       ES6中
       i = i ** 3		表示 i 的 3 次方
    


五、对象

5.1 对象的定义

  1. 对象实际上是我们使用编程方式对某事物的一种抽象表示
  2. 对象当中应当有两种成员变量,一种叫属性,另一种叫方法
    • 事物具有的特性,我们使用属性成员变量来描述
    • 事物具有的行为,我们使用方法成员变量来描述
  3. JavaScript 中的对象是一种简单的hash哈希结构,是一种映射结构,也就是我们常说的键:值对(key: value) 结构,对象这种数据准确来讲是一种数据集合而不是一个单一的数据。
  • key是指变量标识符,可以修改

  • 在哈希结构中key( String 类型)所对应的 value 可以是任意类型,key在 JavaScript 中实际上是字符串类型

    • 对象的特点:
    1. 哈希结构不可逆
    2. 哈希的映射关系唯一:同一个键名只能有一个映射关系(值)
  • 存放的是数据集合

  • 引用类型在比较时,只能比较地址,因为对象是动态类型,无法进行值比较,而值类型比较时,是比较值

  • 采用对象字面量(语法糖)方法创建对象:

var obj={
//  key: value,		键:值 / 值对
    name: "ruby",
    it: {},
    "hair-color": "black",	//内部有特殊字符,必须用引号
    hariColor: "black",  	//小驼峰命名法
    HariColor : "black", 	//大驼峰命名法
    foo: function (val){
        ......
    }
    ......
} 
delete obj.haircolor;	  			    delete 删除into键,只能用于对象属性
console.log(obj[haircolor]);	报错,key是字符串类型,必须有引号
console.log(obj["haircolor"]);	根据语法糖,可以简写成以下形式,若键名中有特殊字符,必须使用[""]来表示
console.log(obj.hairColor);			//‘.’点操作是获取值


5.3对象的遍历

对象只能用 for-in 遍历

  • in是二目操作符,左右各有一个值,不可以更改,用来遍历的o结构
  • o是要遍历的变量标识符
  • 数组也可以用 for-in,下标(键)采用 index 表示
  • 当 key 没有规律时,需要使用 for-in 来遍历
var o={
    one: "11",
    two: "22",
    three: "33"
}var o=new object();
o.one = "11";
o.two = "22";
o.three = "33";
解析:在o结构中我们要依次遍历key变量
for(var key in o){
    console.log(key);
}

结果值: one two three

for(var key in o){
    console.log(o[key]);
}
结果值:11 22 33

in 用来判断一个是否存在于对象当中,返回boolean

console.log(下标 in 变量标识符);		有问题
console.log ("key" in 变量标识符);


六、数组

6.1 数组的定义

数组也是一种集合,一组数组中的数值可以是任意类型的数值,用逗号分隔,用中括号包裹。在javascript 数组是对象中的一种特殊形式。

  1. 数组里面的值,称作数组的元素
  2. 下标从0开始排序
  3. 若数组中有任意元素被删除,或未赋值,会产生唏嘘数组情况,该元素显示为 empty
    • 若元素的值为" ",则表示空白字符(不可见字符)
    • 若元素值为"",表示空字符
  4. 根据语法糖,arr[“2”]可以简写成arr[2],表示arr数组中下标为2的元素
  5. 属性:
    • 每个数组都有length属性,表示数组的长度
var arr=[1, " ", "", 2, true, "!"];
console.log(arr[4]);	打印下标为4的元素

6.2 遍历数组

var arr = ["I", " ", "L", "o", "v", "e", " ", "Y", "o", "u", "!"];
    for(var index in arr){
        console.log(arr[index]);
    }
结果: I Love You!,且每个字符自动换行

数组还可以采用forEach函数来遍历

数组添加元素的方式:

var arr = [];			arr[0] = 1;

6.3 创建数组

现阶段创建数组有两种方法:

  1. 数组字面量,本质是语法糖,通过中括号直接定义数组

    var arr = [1, 2, 3];
    
  2. 要通过构造函数构造出新的数组

    var arr = new Array(element, element, .... element);
    var arr = new Array(length);
    

Array构造函数会因为传入的参数不同而表现出功能的不一致性

给定参数:

  • 当传入多个参数或传入一个非类型的参数,则这些参数将会成为新数组的元素;

  • 若传入的参数个数为1,且为类型,则该参数表示被构造数组的长度,该值必须为无符号整数,否则会报错;

  • 若参数的个数为1,且是类型,则返回该数组是有一个元素的数组,且该元素为类型;

  • 数组可以是一维,二维,多维数组

    var arr = [1, 2, 3];					//一维
    var arr = [1, 2, [1, 2]];				//二维
    var arr = [1, 2, [1, 2, [3, 4, 5]]];	//三维
    


七、函数

7.1函数的定义

  1. function函数定义:为了把某些代码或者某些逻辑,封装在一个结构当中,当需要执行该代码或逻辑时,就可以调用该函数。

  2. 语法:函数的本质就是对象

    除最后一个函数体以外,前面两个都称为参数名,必须以字符串的形式创建
    var f = new Function("arg1", "arg2", "congsole.log(arg1 + arg2);");
    f("h","o");
    
    但是更建议使用语法糖
    function foo(arg1, arg2){
        console.log(arg1 + arg2)
    }
    f("h","o");
    
  3. 对于javascript来说,输入和输出都是可选的。

  4. foo为函数名,函数标识符,命名方式与变量相同

  5. 声明:

    • 必须用 function 关键字声明

    • 如果要使用函数,必须要经过声明,且同变量的声明一样可以声明提前(但函数表达式不具备声明提前特性);一次性声明,次次调用

    • 该语句称为函数声明语句,也称形参列表,用来接收实参的赋值,并封装代码或逻辑

      funciont  foo (a, b, c,....){ 	 }
      
  6. 调用:

    • 函数在调用的时候可以进行输入,即函数的实参,调用时投入的实参需要形参进行接收;函数的输出就是其返回值,在编程语言中输入输出都是可选的

    • 若不调用,则不执行

    • 该语句称为实参列表

      foo(1, 2, 3,...);
      
  7. 例如:

    function add(x, y){		//x,y用于接收数据,功能与变量相同
     console.log(x + y);
    }
    add(44, 88);			//等同于赋值,x=44,y=88
    结果值:132
    
  8. 活动对象 AO

    • 每个函数中都有一个 AO 活动对象,它是执行上下文赞中的一个过程,用来存储当前作用域中可用的数据,当我们需要使用时就会向活动对象中查找。
    • 存储数据顺序: arguments: [形参],函数内的可用函数的声明,函数内的变量
    • 形参中的数据不会与内部的数据发生关系
    • 特殊情况:函数和变量使用同一个标识名(唯一性),变量会失效,不过该变量的值还是存在的,所以当要调用该标识名时,会先采取使用函数而不是变量,若没有则在 arguments 参数中找,否则再向上一层作用域 AO 中查找。
    javascript
    function fn(a) {
      console.log(a); 
      var a = 2;
      function a() {}
      console.log(a);
    }
    
    fn(1);
    

    运行结果:ƒ a() { } 2

    执行步骤:

    1. 调用 fn() 将数值 1 传送给 fn(a)函数中的 a ;
    2. 所有变量都具有声明提前的特性,因此 var afunction a 的声明被系统放在了 fn 作用域的最前端,但因为函数声明提前比变量更早一步,所以var a无效,但它的值还在,所以第一句 console.log() 打印的是 f a(){}
    3. 执行完第一句 console.log() 后,对变量 a 进行了赋值,所以第二句 console.log() 打印的是变量 a 的值。

7.2形参与实参

  1. javascript中实参与形参的个数可以不对称

  2. 现象:

    • 当实参列表多一个数值时,无影响
    • 当实参列表少一个值时,对应的形参结果值为undefined
  3. 函数分有参 / 无参函数、有返回值 / 无返回值函数

  4. 标识符.length:表示形参的个数

  5. arguments对象,

    • 作用:接收实参列表
  • 该对象的结构符合类数组对象,每一个函数体内部都有一个天生的arguments对象,继承于 Object

  • 类数组对象:key 从0 起步依次递增,步长为 1;具有正确的 length 属性

  • 注意:只能在函数内使用,只有在调用时才有用,不同函数之间的arguments互不干扰,它的key无含义

   function foo(){
       console.log(arguments)	//Arguments{...}
    console.log(arguments.__proto__ === Object.prototype)	//true
}
   foo();
  1. return是函数调用表达式的返回值,如果没有定义return输出,则返回值默认为undefined
function add(a,b){	
    console.log(arr.length);	返回形参的个数
    return a + b;
}
//等同于,a=33; b=88; add(33,88)=a+b;也就是result = a+b
var result = add(33, 88);
console.log(result);
结果值:2  121

var result1 = add;
标识符表达式,返回存储的引用地址(返回内部存储的内容)

7.3作用域

  1. 定义:表示某个量所起作用的区域,也可以认为是一个量的生命区域

    重点:javascript中只有函数才能创建作用域

    注意:本质上只有对象才能创建作用域,不过函数作用域与对象作用域有些不同

    对象作用域是用来限制成员变量的

    函数作用域是用来限制普通变量的

    函数:就是函数

    方法:若函数是一个对象(成员变量)的值,那么就称该函数为方法;

  2. JavaScript 中始终会产生一个 Global 作用域在所有作用域的最顶层

    • 中文称它为全局作用域
    • 所有局部作用域都是全局作用域的子作用域。
    • 也可以理解为全局作用域是公有的,局部作用域是私有的。
  3. 作用域分两种:

    • 静态作用域(词法作用域),看声明方式:在变量进行声明时就已经决定了作用域,JS 使用的是静态作用域
    • 动态作用域,看函数的调用形式:函数在哪个作用域中调用,该函数的作用域就是它
  4. 量的查找:若某作用域要使用某个量,那么优先在本作用域中查找该量,若没有,则向上层中查找,若还没有,则向更上一层作用域查找,直到找到Global为止。

    若最终在 Global 也没有找到该量,则会报错。该查找过程只遵循冒泡 (Bubble) 原则。

  5. 函数形参的作用域就是该函数创建的局部作用域。

    var a = 33;
    function f3(a){
    	f3中的a,是局部作用域的形参a,是全局作用域中var a的值,而不是地址,所以并不会影响全局变量a的地址和值
    	console.log(a + 33);
    }
    f3(a);			//输出的是f3中a的值
    console.log(a);	//输出的是var a的值
    结果值: 66 33
    
  6. 作用域链

    定义:根据作用域查找关系,会形成一个查找链条,而这个链条就是作用域链

  7. 闭包

    定义:函数的一种状态。

    作用:使用封闭的作用域可以获得外部自由变量的使用权,**“共享”**外部变量数据。

    查找方式:闭包是沿着作用域链来查找自由变量的,即从该封闭作用域开始向链条的末端开始查找,作用域链的顶端是最深层的局部作用域,末端是Global全局作用域。

    • f3() -> f2() -> f1() -> Global

7.4 函数声明表达式

若把函数当成一个值来操作,那么函数就会变成函数声明表达式,这样的表达式也分为两种:

  • 匿名函数声明表达式:没有标识符 -> 立即执行函数

  • 具名函数声明表达式:有标识符,

    • 具名函数声明表达式的函数名必须在该函数内部使用,为什么这样,是因为具名函数的名字并没有在预解析的时候注册在全局,而是在代码执行时注册在了函数的内部
  • 注意:函数声明语句可以整体提前,只有声明语句提前,函数声明表达式不提前

       var count = 0;
       var foo = function bar(){	//匿名函数声明表达式
       	console.log("--++");
           if(count < 5){
               count = count + 1;
               bar();
           }  
       }
       foo();
       结果值:循环5--++
    
  • 立即执行函数(匿名),使用立即执行函数时,上一条语句必须要有分号结尾

function (){		系统将它当成了函数声明语句,所以会报错
	console.log("---")
}()		

+function (){		+是操作符,两侧必须是表达式,但不标准
	console.log("---")
}()	

;
(function (){		
	console.log("---")
})()	

7.5 Callback回调函数

回调函数:它不由我们手动调用,而是交给某些接口,当某些条件发生时,由接口调用的函数我们称之为回调函数Callback,它并不是指某个函数,而是同一类型

Array.prototype.mysome = function (callback){//callback在这里是形参
    for(i = 0; i < this.length; i++){
        if(callback(this[i],i,this)){
            return true;
        }
    }
    return false;//若没有任何元素满足条件,则返回false
}

var arr = [1,2,3,4,5];
var arr1 =arr.mysome(function(value){//value在这里是形参
    return value > 3;   	//return 返回实参
    //返回给callback,callback接收实参
})//function(value)在这里是mysome的实参,传送给callback
console.log(arr1);

解析:
1function(value)中的value是形参,接收return value>6的返回值(实参)
2function(value)是mysome的实参,返回值传送给function(callback)中的callback形参,若为真则执行if,若为假则不执行
3function(value)是callback的声明,callback(this[i],i,this)this[i],i,this都是实参,传送给value
4、callback接收实参,调用mysome时,for循环语句中的if调用callback(元素,下标,目标数组)for(){ if( this[i]>3 )return true; }
callback(元素,下标,目标数组)用来做测试函数处理

运行过程:
1、当i = 0, if(callback(1, 0, arr)){ return true}中将实参传送给value
value = 1return 1 > 3,返回false,所以不运行if,i = 2
4、当 i = 3时,callback(4, 3, arr),value = 4, return 4>3,返回true,执行if语句,return返回true,最终结果为truereturn value > 6,当i = 4时,callback(5, 4, arr),value = 5,return 5>6返回false,不执行if,i = 6,i>this.length,for循环结束,返回false

7.6 函数的多种形态

  1. 普通函数
  2. 构造函数
  3. 对象、值、方法
function F(){
    this.data = "some data"	//this.data表示在F函数中的变量
}
F.isF = function(o){			//function在这里是对象、方法、值
    return o instanceof this;	//this指向F
}
var f1 = F();           	//返回undefined,F在这里是函数
var f2 = new F();       	//F在这里是构造函数,返回some data
console.log(F.isF(f1));		//false,F在这里是对象
console.log(F.isF(f2));		//true

7.8 异步操作

内存泄漏:当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。

个人解析:当一个对象使用完毕后本该释放空间时,但它依然占据着空间(存放在堆内存中),导致了内存泄漏。

  • JavaScript 无需手动释放内存,有自动垃圾回收机制。

JavaScript 是单线程执行环境:代码是一行一行执行,不可以跳过

但在单线程执行环境中很容易造成阻塞现象,该现象可以用异步操作来解决

用异步操作的场景(只要与时间有关):1、耗时长;2、执行时间不确定

例1:

for(var i = 0; i < 5; i++){
    var delay = 1000 * i;		//同步代码
    setTimeout(function(){  
        console.log(i,new Date);
    }, delay); 
}
console.log("----")

代码解析:
1for循环,congsole.log("----")都主线程代码;创建定时器,本身不是异步操作,内部的回调函数function()才是异步操作。
2for循环并不会等待function执行完毕后再进行i++,它会优先执行完自身的非异步操作(包括var delay = 1000 * i),congsole.log("----")同理
3、在执行for循环时,function()会被踢进“任务队列”中,每当监听时间到了代码就会回归;但因为function()作用域中没有i,所以根据闭包规则在外部查找,最终在全局作用域中找到i,此时的
i = 5for循环已经执行完毕)
4function()会执行5次,声明5次,它的执行时间是可见的,执行时间在非异步代码中,所以每次都返回不同的时间
5、congsole.log("----")function()执行之前,该语句已经执行完毕

运行过程:
var i = 0;	delay = 0S
i = 1;		delay = 1S
i = 2;		delay = 2S
i = 3;		delay = 3S
i = 4;		delay = 4S
i =5;
执行  console.log("----");
function(){		i = 5; new Date	}	delay = 0S
function(){		i = 5; new Date	}	delay = 1S
.....

解决方法(i 的值):

代码解析:function(t) 是立即执行函数,也是主线程代码,i是实参,t接收实参
setTiemout 依赖于外部的 foo 函数,所以 foo 函数不会被释放掉; setTimeout 执行完毕后,就会释放掉它的父级 foo 函数

foo 会被创建 5 次,但因为 JS 中标识名唯一性,会造成后面执行的 foo 函数的返回值会覆盖掉前面的返回值,所以 5 次用的都是同一个函数名(即覆盖)。

若没有 t 进行接收实参,则 setTimeout 还是会找到for 循环中的 i ,此时的 i 已经执行完毕值为 5 ,

for(var i = 0; i < 5; i++){
    (function foo(t){
        setTimeout(function(){
            console.log(t, new Date);
        }, 1000 * t);
    })(i)
}
console.log(i)

for (var i = 0; i < 5; i++) {
 setTimeout(function (t) {
     console.log(t);
 }, 1000 * i, i);	//将函数的实参放在setTimeout的第三个参数上,用 t 来接收
}

例2:

(function f(){
  function f(){return 1;}
  return f();
  function f(){return 2;}
})();				//2

解析:第二个 f 函数声明语句覆盖了第一个



九、深浅拷贝

  • 深浅拷贝的由来:由于 JS 对不同的数据类型分配了不同的内存空间,导致出现了深浅拷贝现象

  • 栈先进后出,只存储静态数据,值类型都存放在栈当中;

  • 堆先进先出,只存储动态数据,对象(又称引用类型)存放在堆当中,非连贯性存储,堆空间中一班存放的都是集合类型(动态类型),集合类型的内部数据结构具有可变性

  • javascript中规定:值类型拷贝的是值,而不是地址,所以值拷贝并不会影响被拷贝元素的地址

    但集合类(对象),拷贝时执行的是引用地址拷贝,而不是值;

    其次要注意变量的值都存储在栈当中,而变量里面存储的都是指向值的地址。

  • 深拷贝:值的拷贝,分配栈空间

    浅拷贝:引用类型的拷贝,具有可变性,例如

  • 在进行数据传递(当数据与标识符进行关联)时,若数据为值类型,那么将会值拷贝一份,然后将拷贝出来的值的地址

    若数据为引用类型,那么将会把标识符里面存储的引用地址拷贝一份并赋给新的标识符

var a = 5;
var b = a;
b = 88;
console.log(a);
结果值:5
在这里b拷贝的是a的值,而不是地址

var ol{
    num = 66
}
var om = ol;
om.num = 99;
console.log(ol.num);
结果值:99
原因:om引用了ol的地址,所以当om中num值发生改变时,ol也会改变


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