自学js第十天:JS对象和this和执行上下文

本文详述JavaScript中的对象创建、this指向及执行上下文的概念。讲解了对象的字面量、构造函数、new关键字的应用,强调了对象在存储和管理数据中的作用。同时探讨了instanceof、构造函数、this的指向规则,并解析了执行上下文的创建、执行和回收阶段,以及变量提升和this的确定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复习数组基础方法 作用域 函数 小测试:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>复习数组基础方法 作用域 函数 小测试</title>
</head>

<body>
  <script>
    /*
      有两个数组 数组A 数组B
      数组A为固定初始数组 ['jojo','okko','张三','秃头','帅小伙']
      数组B为用户动态选择添加的值 [] ... 不确定
      需要 将数组B中前N 位添加到 数组A中 N = 7 - 数组A.length
      数组A 有限制长度 7

      例如 A:  ['jojo','okko','张三','秃头','帅小伙']
          B: ['你好','小脑斧','大西瓜','长得帅']
         输出: ['jojo','okko','张三','秃头','帅小伙','你好','小脑斧'];
    */

    function fillArray(arrA, arrB) {
      var maxLen = 7;
      var arrALen = arrA.length;
      return arrA.concat(arrB.slice(0, maxLen - arrALen));
    }
    console.log(fillArray(['jojo', 'okko', '张三', '秃头', '帅小伙'], ['你好', '小脑斧', '大西瓜', '长得帅']
    ));



    /*
      函数 作用域

    */
    // console.log(x); // undefined
    // f1();
    // var x = 0;
    // function f1() {
    //   f2();
    //   var x = 10;
    //   function f2() {
    //     x = 20;
    //     console.log(x); // 20
    //   }
    // }
    // console.log(x); //0

    // console.log(a); //函数
    // function a() {
    //   a = 20;
    // }
    // a();
    // console.log(a); //20
    // var a = 30;
    // console.log(a); // 30



    function f1() {
      var arr = [];
      //i 
      for (var i = 0; i < 10; i++) { //0 1
        arr.push(function () {// 0 1
          return i; //0 1
        });
        console.log(arr[i]());//
      }
      // console.log(i); //10
      return arr;
    }

    console.log(f1()[0]()); //10


    // function f1() {
    //   var x = 10; //
    //   return function () {
    //     return x;
    //   }
    // }
    // f1()(); //10



  </script>
</body>

</html>

JS之对象 Object:

① obj.属性如果是字面量对象则可以直接obj.属性创建对象属性出来.
②如果是构造函数下的对象, 则obj.属性 如果内部构造函数没有该属性,则返回undefined.
console.log(obj.a);  //直接把a当成键名key了,然后去查询并输出对应的value,没有则返回undefined.

console.log(obj['a']); //这里和上一行一样是指: 查询对象中的key属性a的value 

console.log(obj[a]); //而这里是指: 找a变量的的字面值作为键名key,然后再key输出对应的value,如果a变量未声明则报错,一般用于对象(for key in obj)遍历时使用.特定有变量key的模板

一.对象通俗概念:

什么是Object对象? 咱们要说的对象可不是 女朋友, 在这个世界上 任何具体事物都可以看做 对象
因为他们都有自己的特征、行为。

车子 手机 猫 是对象吗? 
这些都是整体的抽象一类class事物,
只有具体特征和行为的事物才是对象 , 比如猫抽象类,  我家的小黄猫具体对象。

小黄猫
	特征:
        颜色:黄色
        年龄:1岁 
        体重:5kg
        最爱:小鱼干
        名字: 橘子
    行为:
    	吃饭 睡觉 伸懒腰 喵喵喵
	

在这里插入图片描述

2.JavaScript是一门基于对象的语言。

javascript中 我们称Object为 对象 对象的概念也分广义和狭义:
广义对象上:javascript中处处是对象
狭义对象上:指的是我们通过{ }字面量创建的对象。

JavaScript的对象是无序属性的集合。
	①其属性可以包含基本值、对象或函数。对象就是一组没有顺序的值。我们可以把JavaScript中的对象想象成键值对,其中值可以是数据和函数。

    
   ②对象的行为和特征
	特征---属性
	行为---方法
 
 事物的特征在对象中用"各种属性"来表示.
 事物的行为在对象中用"函数方法"来表示。

3.对象属性 和 对象方法:
如果一个变量属于一个对象所有,那么该变量就可以称之为该对象的一个属性,属性一般是名词,用来描述事物的特征
如果一个函数属于一个对象所有,那么该函数就可以称之为该对象的一个方法,方法是动词,描述事物的动作行为和功能

在这里插入图片描述

二.JS的字面量

字面量(literal)是用于表达源代码中一个固定值的表示法(notation).

​ 几乎所有计算机编程语言都具有对基本值的字面量表示, 诸如: 整数, 浮点数以及字符串; 而有很多也对布尔类型和字符类型的值也支持字面量表示; 还有一些甚至对枚举类型的元素以及像数组, 记录和对象等复合类型的值也支持字面量表示法.

字面量(literal),在高级语言中 我们可以通过更直观更高效的方式直接赋予变量 具体 , 当需要使用值得时候 才会去根据值得类型和内容进行包装解析; 先存储 后解释 。

javascript中字面量包括

1. 整数类型字面量(number Literal)

var num1 = 123; 
var num2 = 1.1; 

2. 字符串字面量(string Literal)

var str = '张晓华'; //张晓华 就是字符串文字字面量
var str = '123'; //张晓华 就是字符串数字字面量

3.引用类型数组字面量(array literal)

var arr = [1,2,3,4,5]; //[1,2,3,4,5] 就是数组字面量

4.引用类型对象字面量是一个花括号(object literal) 通过var obj = { }

var obj = {
    name:'橘子',
    age: 1,
    favorite: '小鱼干'
}

/*
{
    name:'橘子',
    age: 1,
    favorite: '小鱼干'
}

就是对象字面量


*/

5.函数字面量(function literal) ,(即匿名函数表达式)

var fn = function(){
    alert('你好');
}

/*
 function(){
    alert('你好');
}
就是函数var fn 的字面量

*/

三.javascript中的对象分类.

JavaScript中的对象分为3种:
1.内置对象、
2.浏览器对象(DOM 和 BOM)、
3.自定义对象.

javascript是基于对象的语言, javascript不可以自己创建一个原生对象,只能访问内置对象属性和调用已有的内置对象方法。但是可以通过基于Object创建狭义概念上的对象
在这里插入图片描述

//JS的内置对象类,对象类内有各种各样的方法.
Number //数字
Boolean // 布尔
String //字符串
Object //对象,老祖宗类
Function //函数引用类型
Array  //数组
Date //时间
Math //数学
Null //空 
RegExp //正则表达式的对象

四.对象创建方式(字面量对象 和 new实例化对象)

(js对象都是通过key : value冒号+键值对赋值的)

创建对象有三种基础方式:
1.对象字面量:
2.原生单个对象实例化: ( 升级:工厂模式对象)
3.自定义构造函数:
  • 静态的字面量 去创造对象. var 变量 = { 字面量key:valule }
var myCat = {
    name: '橘子',//特征
    color: 'orange',//特征
    age: 1,               //特征
    favorite: '小鱼干',  //特征
    speak: function (){  //行为
    console.log('喵~~喵~喵~~~');
    }
 }
console.log(myCat);
    //PS:很神奇 ,只要引用修改并且赋值了,哪怕是未定义的属性或行为都算是创建为对象的属性.
myCat.sex = true; //引用.创建属性值
myCat.age = 3; //引用.修改属性值
myCat.speak(); //引用.调用行为方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJ8TkWoR-1620235668986)(C:\Users\tuyue\AppData\Local\Temp\1615950076523.png)]

  • 动态原生单个对象实例化 (一个一个单独new , 几个对象就new几次 )
//格式:var 变量 = new Object内置对象()
var myCat = new Object();
myCat.name = '橘子';  //和java一模一样的访问(引用.的方式)
myCat.color = 'orange';
myCat.age = 1;
myCat.favorite = '小鱼干';
myCat.speak = function(){
 console.log('喵~~喵~喵~~~');}
  • 工厂函数创建对象 (封装进一个fn函数工厂内, 调用一次函数就new一个Obj对象,减少代码重复性):
    (升级版的原生实例化对象 ,调用一次就进入函数工厂new一个,代码效率高,当然只new一个原生即可)
//案例1:关于createCa函数类的封装
function createCat(name,age,color){
    var cat = new Object();
    cat.name = name;
    cat.color = color;
    cat.age = age;
    cat.favorite = '小鱼干';
    cat.speak = function(){
    console.log('喵~~喵~喵~~~');
    }
    return cat;  //切结写完要return 返回引用 ,返回cat引用变量,即返回new Object();
}

var myCat = createCat('橘子',1,'orange'); //通过形参和实参对应来赋值
var myCat1 = createCat('小黑',1,'black');
var myCat2 = createCat('小黑',1,'black');


//案例2; 关于 createstudent函数类的封装
function createStudent(name, age, sex) {
      var obj = new Object(); //new 构造函数();
      obj.name = name;
      obj.age = age;
      obj.sex = sex;
      obj.speak = function () {
        console.log('我要学习,学习使我快乐,学习让我成长!');
      }
      return obj;
    }
    
var stu1 = createStudent('小明', 99, 0,);
 stu1.age =10; //修改
var stu2 = createStudent('小里', 99, 0,);
var stu3 = createStudent('小王', 99, 0,);

console.log(typeof stu1); //object ,类型还是obj,无法通过typeof判断具体对象,全部都是object类型
console.log(stu3 instanceof  Object); //true,只能通过instanceof亲子鉴定布尔,来判断对象.
  • construct构造函数创建对象:(给自定义对象构造函数 + this + new实例化 一起搭配才行 ,最常用)

    1.javascript 中没有类 用javascript 做面向对象开发OOP 是用js原型来模拟构造函数

    2.构造函数名首字母是大写的 名词 (模拟构造函数,大写的方法名(类似类名))

    
      构造函数 模具 工厂 孵化器
      1. 本质上还 是fn函数 只是构造函数名字是首字母大写, 而普通函数是小写的函数名.
      2. 构造函数实例化需要 new 关键字
      3. 函数体内使用this关键字,代表所要生成的对象实例。
    
    
//无参构造函数写法:
function fun() { 
  this.name = 'mm';
}
var obj = new fun(); 
console.log(obj.name); // mm 


function Cat(name,age,color,favorite){ //有参构造
    this.name = name;
    this.age = age;
    this.color = color;
    this.favorite = favorite;
    this.speak = function(){
    console.log('喵~~喵~喵~~~');
    }
    
    //隐式的 return this;
}
var myCat = new Cat('橘子',1,'orange','小鱼干');

    
   
//有参构造函数写法 ,(自定义对象的自建加工厂,只能用this.形式 ,和java一样this指向当前所处的类对象.)
function Student(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
      this.speak = function () { //
        console.log('我要学习,学习使我快乐,学习让我成长!' + '我叫' + this.name);
      }
      //隐式的 return this;
    }

    var stu3 = new Student('张三', 18, 1);
    console.log(stu3.name); //张三
	console.log(this.name); //空白 window.name

    var stu4 = new Student('李四', 18, 1);
    console.log(stu4.name);  //李四
	console.log(this.name); //空白 window.name

    console.log(typeof stu3); //object ,类型还是obj,无法通过typeof判断具体对象,全部都是object类型
    console.log(stu3 instanceof Student); //true,只能通过instanceof亲子鉴定布尔,来判断




/*
this :
 0. this是 对象 的代指 this永远指向对象 this是动态乱走的, 不到最后一刻, 无法确定具体指向哪个对象
 1. 函数中的this指向 ?       谁(对象)调用这个函数 this指向谁
 2. 构造函数中的this指向谁 ?  this永远指向构造函数实例化的对象
*/
总结 : 字面量创建对象 和 new实例化对象 的优劣:

-->

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>总结知识点</title>
</head>

<body>

  <!-- 

    包装对象 instanceof
      javascript中 分基础类型和对象类型
        null undefined string number boolean 

        array object

        function

      基础内置对象
        String Number Boolean Array Function Object Null RegExp Date Math JSON Error Arguments 


      字面量 
        直接的书写"值" 在存储的时候 不考虑他的构造对象 
        使用的时候 会通过包装对象进行包装 使用后 销毁包装对象

      new实例化对象 和 字面量赋值对象区别:
        1. var={}字面量单纯的值 存储更迅捷 占用空间更少 栈空间存储 值  (实际开发常用这种创建对象)
        2. new 实例化对象 出生的时候就拥有对应构造函数内置属性 方法 完整的对象 对象


      临时包装类调用 :
        1.字面量形式会临时生成一个包装对象来帮助完成属性调用和方法调用 然后包装对象自我销毁

        2.实例化对象  出生的时候就拥有对应的构造函数赋值和内置属性可以直接用, 因此也不需要包装对象


      


   -->
   <script>
     
     //字面量创建对象(推荐)
     var a ={
       x:1,
       y:2,
       z:3
     }
     //临时包装类,字面量是不能直接.调用的
     // var obj = new Object(a);
     //obj.x=1;
     a.x=1



    // new实例化对象  
    //出生的时候就拥有对应的构造函数赋值和内置属性可以直接用, 因此也不需要包装对象
     function fun() { 
    this.a = 'mm';
}
    var obj = new fun(); 
    console.log(obj.a); // mm 


   </script>
</body>

</html>
扩展:(java这么直接 var cat = new Object()是报错的,属于父->子强转 MyCat myCat = (MyCat)0bj )
//java直接像上面这么new是报错的,因为不支持父亲->儿子,只能强转
Object obj =new Object();
if(obj instanceof Animal02) {  //对象是一个动物,则强转为动物.
Animal02 a6 =  (Animal02)obj;
}

/* 1、复习:方法覆盖和继承的区别?继承全部。只覆一(除构造方法都继承,但是覆盖重写只针对实例方法)
 * 相同点;(构造方法都不能继承和覆盖)
 * 不同点;覆盖是在继承的关系下去只覆盖重写实例方法,(构造和私有和静态不能)
 * 继承是除了两类之间除了构造方法,其他全部继承过来。
 *  
 * 2、多态分为(向上和向下)
 * 向上; Animal a3 = new Cat();    (访问父类已提供覆盖的方法,子类是必有的,因此自动转无风险)  
 *       Animal02 a3= new Cat02(); 
 * 、向下;Cat02 c2 = (Cat02)a3;     (访问子类特有方法时,父类无。要从“子找父”变为“父找子”)  —— (前提保证父类之前指向的对象就是强转后的对象,不然运行异常) 
 * “父找子”这种方式叫做“向下强制转型”。  向下转型,必须加强转符,强制类型转换 
 *  等式右边父类对象类赋值给左边子类,子类就会指向父类对象                          
 * 注意;有风险,不知道a5父类指向的儿子对象是不是和要转型的儿子对象是同一个儿子对象, 有可能父亲有多个儿子。因此要做instanceof亲子鉴定
 *              
 * 
 * 一个合格的程序员。只要用向下转型。都要亲子鉴定instanceof运算符,
 * 3、instanceof 亲子鉴定运算符;(父类指向的儿子要和转的儿子是同一对象)
 * 作用;可以判断父类引用指向的对象类型;
 * 语法结构;if(引用 instanceof 对象类型 )
 *       Animal02 a3 = new Cat02(); 
 *     if(a4 instanceof Cat02 ){    //如果a4引用是一个Cat02类型,则可以强转     
 * 		     Cat02 c3 = (Cat02)a4; //强转内容
			 c3.catchMouse();	
 * }  
 * 
 * 4、为什么要用instanceof运算符呢?(超级重用的是什么对象。
 * Animal02 a3 = new Cat02();/Animal02 a4 = new Dog02();这样合起来写你是直接肉眼看得到的,这种方法体的多态。
 *                                                                                                   但是有可能是参数多态断开写的你是不能直接看到的,多人分工合作。
 *举例自己写class Test{                                                  
 * 	public void AnimalTest(Animal a3){  你不知道别人在调用你这个方法的时候会给你传什么子类过来,可以给你一个Dog02也可以给你一个Cat02                                                        
 *       if(a3 instanceof Cat02) {    //解决办法, 写好传猫的情况                                                                                                  
			Cat02 c2 = (Cat02)a3;   //动物是个猫就强转为猫
			c2.catchMouse(); 
		}else if(a3 instanceof Dog02){       //动物是个狗就强转为狗
			Dog02 d1 = (Dog02)a3;
			d1.bark();
			}
 * }
 * }
 * 轮到别人写;比人在别的类里面调你写的方法
 * Test a1 = new Test();    猫和狗得和动物得有继承记得,
 *  a1.AnimalTest(new Dog02());   传过去相当于Animal a3 = new Dog02()参数多态 
 *  a1.AnimalTest(new Cat02());   传过去相当于Animal a3 = new Cat02()参数多态 
 */
public class 多态的向下转型instanceof使用 {
	public static void main(String[]args) {		
//		Animal02 a3 = new Dog02();// 这里编译没问题。但是运行异常了。Exception in thread "main" java.lang.ClassCastException: 多态.Dog02 cannot be cast to 多态.Cat02
//		Cat02 c2 = (Cat02)a3;   //没有保证到父类a3之前向上转型指向的对象和强制的对象是同一个对象。要做instanceof亲子鉴定
//		c2.catchMouse();        //这里具体就是父亲之前指向的儿子是个小狗。但是却让他强行把狗转为猫去抓老鼠。不行的。啪啪啪
		 
		
		Animal02 a3 = new Cat02(); 
		if(a3 instanceof Cat02) {      //因此每个程序员在向下转型是,都要instanceof亲子鉴定,确定是多态对象为猫才能去让猫去抓老鼠
			Cat02 c2 = (Cat02)a3; 
			c2.catchMouse(); 
		}else if(a3 instanceof Dog02){       
			Dog02 d1 = (Dog02)a3;
			d1.bark();
		}
		
		
		Animal02 a4 = new Dog02();   //这里是小狗在吠叫
		if(a4 instanceof Cat02) {
			Cat02 c2 = (Cat02)a4; 
			c2.catchMouse(); 
		}else if(a4 instanceof Dog02){ 
			Dog02 d1 = (Dog02)a4;
			d1.bark();
		}
		
	}
}

class Animal02{
	public void move() {
		System.out.println("动物在移动");
	}
}

class Dog02 extends Animal02{
	public void move() {
		System.out.println("小狗在啪啪");
	}
	public void bark() {
		System.out.println("小狗在吠叫");
	}
} 

class Cat02 extends Animal02{
	public void move() {
		System.out.println("猫儿在走猫步");
	}
	public void catchMouse() {
		System.out.println("Tom猫在抓Jerry");
		}
	}

五.JS对象的简单应用案例:

我们为什么需要对象?

1.具体作用:对象就像一个java中的hashMap无序集合容器,能够把一系列的值通过一个对象形成无序不可重复的储存集合,集合可以方便去调用 和 管理(增删查改).
2.数组只能通过下标寻找 , 而对象可以通过更具体的键值对key:value一一对应寻找
3.最终都要通过 局部引用. key ;调用出来
①赋值: key : value .
②调用/修改: 局部引用 . key = xx;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>对象应用</title>
  <style>

  </style>
</head>

<body>
  <script>


 
    //对象简单应用1:(统一管理,可以各自调用各自变量,不会乱)
    var xh = {   //小花具体对象
      name: '小花',
      age: 19,
      sex: '男'
    }

    var zs = {   //张三具体对象
      name: '张三',
      age: 12,
      sex: '女'
    }
 	//可以各自调用和输出各自的名字,不会弄乱,否则要分开函数去输出
    console.log('我的名字' + xh.name);   
    console.log('我的名字' + zs.name);  



    //对象简单应用2: (可以 return 对象(多个值))
    //一个函数 需要返回 数组的每项和sum以及平均值avg
    function getResult(arr) {
      var sum = 0, avg = 0;
      for (var i = 0, len = arr.length; i < len; i++) {
        sum += arr[i];
      }
      avg = sum / len;

      return {        
        "avg": avg,  
        "sum": sum    
      }
     //上述返回的是一个{} 对象,这样就可以返回多个数值.   
    //如果一次性return sum,avg; 则只会返回最后一个avg.很麻烦    
    //如果一次性返回一个容器数组[]也行,但是不能用键值对形式,只有结果.这样不知道结果具体是什么信息.
    }

    var result = getResult([1, 2, 3]); //返回值变成一个对象,这样就可以返回多个值,否则return只能返回一个值.
 /*    var result ={       //间接等于  
        "avg": avg,    
        "sum": sum  }  */
    console.log(result)    
    console.log(result.avg) //2
    console.log(result.sum)  //6
    console.log(getResult([1, 2, 3]).avg);//2
    console.log(getResult([1, 2, 3]).sum);//6



    

    //对象简单应用3: (对象可以当成方法实参,一次性输出一个整体形参param)
      
    //以前正常的写法,形参和实参一一对应,都要分开一个一个参数写.
    function showMe(name, age, sex, height, weight) {
      console.log('大家好,我叫' + name + '我今年:' + age + '我性别:' + sex + '身高' + height + '体重' + weight);
    }
    showMe('张三', 13, '男', 180);


    
   // 现在把对象分为 (var xx方法形参)  和  ({}调用方法实参)  
    //可以放一个对象引用param, 直接梭哈对象全部信息.
   // var param = {xx:值 xx:值 xx:值};
    function showMe(param) {
      console.log('大家好,我叫' + param.name + '我今年:' + param.age + '我性别:' + param.sex + '身高' + param.height + '体重' + param.weight);
    }

    showMe(
        {
      name: '张三',
      age: 13,
      sex: '男',
      weight: '180'
    }
    );




  </script>
</body>

</html>
案例2的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5twf1BR3-1620235668991)(C:\Users\tuyue\AppData\Local\Temp\1615952952504.png)]

六.深入instanceof (亲子鉴定,只用于鉴定对象)

在 JavaScript 中,判断一个变量的类型常常会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。例如:

var oStringObject = new String("hello world");
 console.log(typeof oStringObject); //object
console.log(typeof string); //undefined
console.log(typeof 'string');//string
console.log(typeof String); //Function
console.log(oStringObject instanceof String);      // 输出 "true"
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>instanceof</title>
</head>

<body>
  <script>

    
    var num =1;
    console.log(num instanceof Number); //false 
    //因为字面量num是基本类型number,而不是Number包装类对象,因此

    /*  
      var numObj = new Number(num);
       numObj.toString() 
       以上是js引擎帮我们自动num字面值转为包装类Number临时对象
       才可以让num去调用toString()方法,其实是包装类对象去numObj.toString()最后,才等于字面量 num.toString()
      */
    //numObj=null
    console.log(num.toString()); //'1' 
    //那为什么这里字面量却可以调用方法呢? 只有对象才能调用方法的吧?
    //原理:JS和java不同,js可以直接字面值变量去 . 调方法,
    //因为基本数据类型number在使用 点.调用 的时候js会帮我们自动生成一个Number包装类临时对象,
    //.调用用完会马上null自动销毁对象,如下.
    
      


    //相反如果字面量是一个真实的对象.则可以直接instanceof亲子鉴定对象类型.
    //想要是true,必须得是'对象'才可以用instanceof亲子鉴定比较.
    var numObj = new Number(1);
    console.log(numObj instanceof Number);  //true 
  

   


  
    
    //string基本类型案例:
    var str = 'N好啊';

    //下面是看不见隐式的生成,JS不会告诉你.
    // var strObj = new String('N好啊');
    // strObj.a = 'wob'; 
    //strObj.toLowerCase()
    str.a = 'wob';
  
    // strObj = null;  调用完临时对象,立马销毁,轮到当前的字面值去输出.
    console.log(str.a); //undefined  
    console.log(str.toLowerCase());  //n好啊 
  
   // 这里和上面Number一样,基础类型string的字面值在使用.的时候
    //其实JS自动是临时包装对象String类,然后其实是对象String的a属性,因此String内部没有a属性,
    //而自己不能自定义内置a属性,因此只能当成普通定义的var a;结果是undefined,
    //而.toLowerCase()方法是内置,String可以直接调用.
    


    //总结: 基本类型 VS 引用类型
    var str = 'fdsfsd'; //一个值    字面量值也不是一个对象 string基本类型的字面量

    var strO = new String('fdsfsd');//一个对象 一出生就有各种标准属性和方法

    //在 字面量值直接进行对应类型方法或者属性调用的时候 
    // 会套上一层包装对象来做 做完之后 包装对象销毁

    console.log(strO, str.toLocaleLowerCase()); // String "fdsfsd"

    // 对象 是否是 某个构造函数厂生产的
    console.log(str instanceof String); //false  (无包装过的)
    console.log(strO instanceof String); //true  (有包装过的)


   
  </script>
</body>

</html>

六.深入构造函数(constructor)

1.面向对象编程’的第一步,就是要new生成对象。而js中面向对象编程是基于构造函数(constructor)和原型链(prototype)的。

2.前面说过,“对象”是单个实物的抽象。通常需要一个类模板,表示某一类实物的共同特征,然后“对象”根据这个模板生成。

3.js语言中使用构造函数(constructor)作为对象的模板。所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构模板(比如都有名有姓)。

function Person(name,age,sex){ //Person就是构造方法函数
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.speak = function(){
        console.log('我叫' + this.name + ',今年:' + this.age + '岁,性别:' + this.sex);
    }
}

var p1 = new Person('小李',11,1);
var p2 = new Person('小里',11,1);
var p3 = new Person('小名',11,1);
//构造函数注意点:
  a:构造函数的函数名(类名)的第一个字母通常大写。

  b:函数体内使用this关键字,代表所要生成的对象实例。

  c:且生成对象的时候,必须constructor构造 + new命令才能调用构造函数。

七.了解new关键字.

(使用new即会自动调用对象函数,进入构造函数去赋值生成一个对象,然后this指向该对象,最后return返回给new的左边的var变量)

构造函数 ,是一种特殊的函数。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

  1. 构造函数用于创建一类对象,首字母要大写。
  2. 构造函数要和new一起使用才有意义。

new在执行时会做四件事情

1 创建一个空对象,作为将要返回的对象实例化。

2 将空对象的原型指向了构造函数的prototype属性。(指针引用)

3 将空对象赋值给构造函数内部的this关键字。 (this成为当前对象)

4 开始执行 构造函数内部的代码。

九.对象集合的遍历和增删查改

1.增删查改:

var obj = {}; 
obj.name = 'object1'; //对象增加属性.  (java不可以这么干,js可以,没有该属性直接当成增加属性)
obj.name = 'object1'; //对象修改属性.
obj['name'] = 'object'; //查询对应key名,的value.
delete obj.naem; //删除对象属性

1.遍历对象的属性. (遍历hashmap)

一般通过for…in语法可以直接遍历一个对象

ps:注意以下三种写法的区别:
console.log(obj.a); //直接把a当成键名key了,然后去查询并输出对应的value.
console.log(obj[‘a’]); //这里和上一行一样是指: 查询对象中的key属性a的value
console.log(obj[a]); //而这里是指: 找a变量的的字面值作为键名key,然后再key输出对应的value,如果a变量未声明则报错,一般用于对象遍历时使用.特定模板

//复习arr数组如何遍历的:

   var suspects = ['张三', '李四', '王五', '赵六']; //4 
    
   //1.普通for循环
	for (var i = 0; i < suspects.length; i++) {// 0 - 3 
      console.log( suspects[i]);
      if (suspects[i] === '王五') {
        console.log('就是他:'+ suspects[i] + ","+ (i+1) + '号嫌疑人');
      }
    }

    //2.ES6: for idx in   有可能乱序 (下标变为idx)
    for (var idx in suspects) {
      console.log(idx, suspects[idx]);
    }
    /* 0 张三
      1 李四
      2 王五
      3 赵六 */

    //3.ES6: for item of  (不推荐,es6讲为什么有undefined))
    for (var item of suspects) {
      console.log(item , suspects[item]);
    }
      /* 张三 undefined
        李四 undefined
        王五 undefined
        赵六 undefined */






	//对象的遍历 ,不能用普通for了,因为没有下标..
 	/* 
 	  数组VS对象:
 	  1.
      数组 是有序的元素集合 有长度 有下标 0 1 2 3 4....
      对象 是无序的元素的集合 没有长度 没有下标.
      2.
      arr[idx]  是指:查询数组对应的idx下标
      obj['key']  是指:查询对象中的key属性的value  , 这个而实际等于 obj.key的
     */


	


	//案例1: 提前了解知识点.
    var obj = {
      a: 1,
      b: 2,
      c: 3
    }
	obj.b = 5;
    console.log(obj.b); //修改为5
    obj['b'] = 100;
    console.log(obj.b); //这种也可以修改为100
	
	//ps:注意以下三种写法的区别:
     console.log(obj.a);  //直接把a当成键名key了,然后去查询并输出对应的value.
     console.log(obj['a']); //这里和上一行一样是指: 查询对象中的key属性a的value 
	 console.log(obj[a]); //而这里是指: 找a变量的的字面值作为键名key,然后再key输出对应的value,如果a变量未声明则报错,一般用于对象遍历时使用.特定模板
	
    

	

 	//案例2: for..in进入对象遍历,特定模板.
	var obj = {
      a: 1,
      b: 2,
      c: 3
    }
    for (var key in obj) {
       console.log(key, obj[key]);
        key 属性名称 obj[key] 属性值 key 变量存储的是字符串 'a' 'b' 'c'
        
        //以下两种写法是不对的遍历输出写法,上面有案例解释为什么?.
       //console.log(key, obj.key);  //声明了,但也是输出undefined
       //console.log(key,obj['key']);//声明了,但也是输出undefined
    }
 	 /* a 1
        b 2
        c 3 */


2.删除对象的属性:(删除之后,再输出不会报错只是undefined)

function fun() { 
  this.name = 'mm';
}
var obj = new fun(); 
console.log(obj.name); // mm 
delete obj.name;
console.log(obj.name); // undefined

十.JSON (只能存 js数据文件格式)

1.JSON概念:

JSON即Javascript对象表示方法 (Javascript Object Notation) ,也就是通过字面量来表示一个对象:

JSON 英文全称 JavaScript Object Notation
JSON 是一种轻量级的数据交换格式。
它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集

2.JSON 语法格式:

//(类似java的properties配置文件,key一定要字符串)

数据使用键名/值对表示,键名是字符串,值没有限定;
例如 “language”:”Java”
每个数据之间由逗号分隔;
使用大括号保存对象,对象可以包含若干个数据;
使用方括号保存数组,数组值使用“,”分割;

JSON数据使用’”键名”:”值”’的形式,其中键名要求是字符串,而值 可以是以下任意类型:
1. 数值(整数,浮点数)
2. 字符串(在双引号中)
3. 逻辑值(true/false)
4. 数组(在方括号中)
5. 对象(在花括号中)

3.JSON 示例:

//一个p标签的css样式,用js实现
var nodeDate = {
	"NodeName" : "P",
    "NodeType" : "Node.ELEMENT_NODE",
    "NodeId" : "",
    "NodeClassName" : "des",
    "NodeStyle": "text-align:center;color:blue;font-size:22px;text-indent:2em;",
    "NodeContent":"哈哈哈哈",
    "NodeChildElement" : []
}
JSON不能存变量进去,只能存变量值即数据,否则报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ii5OGwW6-1620235668994)(C:\Users\tuyue\AppData\Local\Temp\1616256961130.png)]

//存对象的数组
var studentList = [
    {"name":"张三",'age':18,'sex':1},
    {"name":"李思",'age':19,'sex':0},
    {"name":"王武",'age':98,'sex':1},
    {"name":"赵柳",'age':77,'sex':0}
]
//echar 立体图片的内置json代码
var chinaMap = [
      {
        "code": "33",
        "name": "浙江省",
        "children": [
          {
            "code": "3301",
            "name": "杭州市"
          },
          {
            "code": "3302",
            "name": "宁波市"
          },
          {
            "code": "3303",
            "name": "温州市"
          }
        ]
      },
      {
        "code": "32",
        "name": "湖南省",
        "children": [
          {
            "code": "3201",
            "name": "长沙市"
          },
          {
            "code": "3202",
            "name": "株洲市"
          },
          {
            "code": "3203",
            "name": "湘潭市"
          }
        ]
      }
    ]             

4.JSON方法API接口: (十分好用)

①JSON.stringify() 序列化 [obj对象 -> JSON字符串]
1.方法将一个 JavaScript 对象 或 对象值解析转换为 JSON 字符串形式,能够在网络上传输数据

2.如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性。

//所有的例子都不能带有var变量,因此json数据格式只能存数据,不能带变量

//静态对象
console.log(JSON.stringify({ x: 5, y: 6 }));
//  "{"x":5,"y":6}"


//静态对象
console.log(JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }));
//  "{"x":[10,null,null,null]}"



//new动态date类对象
console.log(JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)));
//  ""2006-01-02T15:04:05.000Z""


//数组内的对象
console.log(JSON.stringify([new Number(3), new String('false'), new Boolean(false)]));
//"[3,"false",false]"






参数
JSON.stringify(value[, replacer [, space]])
value
将要序列化成 一个 JSON 字符串的值。
replacer 可选
如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。
space 可选
指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null),将没有空格。
返回值
一个表示给定值的JSON字符串。
异常
当发现循环引用时,抛出类型错误TypeError(“循环对象值”)异常。
当试图将BigInt (BigInt 为javascript中Number能表示的最大数 2^53 - 1 )值字符串化时,会抛出类型错误 TypeError(“BigInt值不能在JSON中序列化”)。


②JSON.parse() 反序列化

将JSON数据解析回为Javascript对象

该方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。提供可选的 reviver 函数用以在返回之前对所得到的对象执行变换(操作)。

var json = '{"result":true, "count":42}';
var obj = JSON.parse(json);

console.log(obj.count);
//42

console.log(obj.result);
// true

参数
JSON.parse(text[, reviver])

text
要被解析成 JavaScript 值的字符串
reviver 可选
转换器, 如果传入该参数(函数),可以用来修改解析生成的原始值,调用时机在 parse 函数返回之前。
返回值
Object 类型, 对应给定 JSON 文本的对象/值。
异常
若传入的字符串不符合 JSON 规范,则会抛出 SyntaxError 异常

十.执行上下文 (超级难! , 和函数预解析有关)

一.什么是执行上下文:

简而言之,执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行

二.执行上下文的类型:(执行上下文总共有三种类型:)
  • 全局执行上下文:(srcipt标签内的解析)

    这是默认的、最基础的执行上下文。不在任何函数中的代码都位于全局执行上下文中,它做了两件事:

    1. 创建一个window全局对象,在浏览器中这个全局对象就是 window 对象。
    2. 再将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。(虽然可以有多个srcipt,但是都当成一个)
     /*
          由上至下 一句 一句 执行代码,(但是是预解析之后的那种顺序,不是我们书写在js中的顺序)
    
          1. 创建一个全局对象,在浏览器中这个全局对象就是 window 对象。
          2. 将 this 指针指向这个全局对象。一个程序中只能存在一个全局执行上下文。
    
    
          ta this
          张三和李四走在乡间的小路上,突然李四停下了脚步,
          **他**低头望着土地. (他是指李四,为什么?,因为是前面指出主语李四停下脚步...)
     */
    
        a = 10;
    
        a = a || 20; //true || 20
    
        console.log(a); //10
    
        a = function () {
          console.log(this); //Window
          console.log(this.a);  //function () {}
          //a函数内的this,  就看this所处的a函数被谁调用,一看是默认window调用,则this指向全局window.
          
        }
    
        a();
    
        console.log(window.a); //function () {}
    	//函数名和变量名重名了,外部会覆盖变量名输出函数体自身.
    
  • 局部函数执行上下文: (局部函数内部的解析)

    每次调用函数时,都会为该函数创建一个新的执行上下文。每个函数都拥有自己的执行上下文,但是只有在函数被调用的时候才会被创建。一个程序中可以存在任意数量的函数执行上下文。每当一个新的执行上下文被创建,它都会按照特定的顺序执行一系列步骤,具体过程将在本文后面讨论。

    function fn() {
          console.log(this.name); //this所在的fn是被window调用的,因此是全局name 的ls
        }
        fn();  //1s
    
    
        var obj = {
          name: 'zs',
          fn: function () {
            console.log(this.name); //this所在的fn是被window调用的,因此是全局name 的ls
          }
        }
    
        var name = 'ls';
    
        fn(); //ls
    
        //创建一个执行上下文环境
    
    
  • Eval 函数执行上下文:

    运行在 eval 函数中的代码也获得了自己的执行上下文,但由于eval是魔鬼 我们一般不通过eval进行开发操作。

      /*
          eval 性能黑洞 
          没必要使用 
        */
    	//eval会解析内部字符串内容为js代码,并执行.
        eval("console.log('我是eval')");  //解析con出来,并执行con
    	//我是eval
    
        var str = function () {
          var b = 10;
          console.log(b);
        }
    
       eval("str();");  //解析str出来,并执行去调用str()方法
    	//10
    
    
三.执行上下文的生命周期:

执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段,本文重点介绍创建阶段。

①创建阶段

当函数被调用,但未执行任何其内部代码之前,会做以下三件事:

  • **创建变量对象:**首先初始化函数的参数 arguments,提升函数声明和变量声明。下文会详细说明。
  • **创建作用域链:**在执行期上下文的创建阶段,作用域链是在变量对象之后创建的。作用域链本身包含变量对象。作用域链用于解析变量。当被要求解析变量时,JavaScript 始终从代码嵌套的最内层开始,如果最内层没有找到变量,就会跳转到上一层父作用域中查找,直到找到该变量。
  • **确定 this 指向:**包括多种情况,下文会详细说明

PS:在一段 JS 脚本执行之前,要先解析代码(所以说 JS 是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来。变量先暂时赋值为 undefined,函数则先声明好可使用。这一步做完了,然后再开始正式执行程序。

另外,一个函数在执行之前,也会创建一个函数执行上下文环境,跟全局上下文差不多,不过 函数执行上下文中会多出 this arguments 和函数的参数。

②执行阶段

执行变量赋值、代码执行

③ 回收阶段

执行上下文出栈等待虚拟机回收执行上下文

function a() {
      var x = 1;
      return function () {
        x++; //持有上级作用域的变量
        return x;
      }
    }
    var w = a();//这里会让a函数调用完结果不会被GC回收,还保留着++,
    console.log(w()); //2
    console.log(w()); //3
    console.log(w()); //4
    console.log(w()); //5

        
    //柯理化函数触发GC,两个括号,a()返回的结果函数fn,再次调用,调中调.
    //每次调用完会回收,重置不会影响下一次
    console.log(a()()); //2
    console.log(a()()); //2
    console.log(a()());//2
    console.log(a()());//2


四.深入变量提升 和 this 指向的细节

1. 变量声明提升

大部分编程语言都是先声明变量再使用,但在 JS 中,事情有些不一样:

console.log(a); // undefined
var a = 10;

上述代码正常输出undefined而不是报错Uncaught ReferenceError: a is not defined,这是因为声明提升(hoisting),相当于如下代码:

var a; //声明 默认值是undefined “准备工作”
console.log(a); //undefined
a = 10; //赋值
console.log(a); //10
2. 函数声明提升

我们都知道,创建一个函数的方法有两种,
一种是通过函数声明function foo(){}
一种是通过函数表达式var foo = function(){} ,
那这两种在函数提升有什么区别呢?

console.log(f1); // function f1(){}
function f1() {} // 函数声明
console.log(f2); // undefined
var f2 = function() {}; // 函数表达式
接下来我们通过一个例子来说明这个问题:
function test() {
    foo(); //报错, 未捕获类型错误“foo不是函数”,此时还foo不是函数,还是一个undefined的变量而已.直接报错
    bar(); // "this will run!"
    var foo = function() {
        console.log("this won't run!");
    };
    foo(); //这里才不会报错,因为已经foo已经 ,已经赋值为函数了.
    function bar() {
         console.log("this will run!");
    }
}
test();

//全局解析
//声明
// var foo;
//function bar()

//执行
// foo();
//bar();
//foo = function() {}
// foo();

在上面的例子中,foo()调用的时候报错了,而 bar 能够正常调用。

①我们前面说过变量和函数都会上升,遇到函数表达式 var foo = function(){}时,首先会将var foo当成变量声明上升到函数体顶部,然而此时的 foo 的值为 undefined而还没有赋值为一个函数体 , 所以执行foo()不可能调用而报错。

②而对于函数bar(), 则是提升了整个函数声明,所以bar()才能够顺利执行。

③有个细节必须注意:当遇到函数和变量同名且都会被提升的情况,函数声明优先级比较高,因此变量声明会被函数声明所覆盖,但是可以重新赋值。(但实际开发也没谁会把函数名跟方法名重名…逗比)

alert(a); //输出a函数:function a(){ alert('我是函数') }
function a() {
    alert("我是函数");
} //
var a = "我是变量";
alert(a); //输出a变量:'我是变量'

function 声明的优先级 比 var 声明高,也就意味着当两个同名变量同时被 function 和 var 声明时,function 声明会覆盖 var 声明,上面这段代码等效于:

function a() {
    alert("我是函数");
}
var a; //hoisting
alert(a); //输出:function a(){ alert('我是函数') }
a = "我是变量"; //赋值
alert(a); //输出:'我是变量'

④最后我们看个复杂点的例子:

function test(arg) {
    //此时有个隐式的var arg;
    // 1. 形参 arg 是 "hi"
    // 2. 因为函数声明比变量声明优先级高,所以此时 arg 是 function
    
    console.log(arg); //本来应该是undefined的,但是重名就会覆盖声明变量var arg;从而输出函数体.
    var arg = "hello"; 
    function arg() {
        console.log("hello world");
    }
    console.log(arg); //此时被赋值为全局的"hello "
}
test("hi");
/* 输出:
function arg(){
    console.log('hello world') 
    }
hello 
*/

这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后依次按照如下的步骤执行:

  • 如果有形参,先给形参赋值
  • 进行私有作用域中的预解释,函数声明优先级比变量声明高,最后后者会被前者所覆盖,但是可以重新赋值
  • 私有作用域中的代码从上到下执行

五. 确定 this 的指向(分两种位置的this)

了解this:!!! this.key ( this其实都相当于已经是var声明,因此不会报错,只是undefined而已 )

JavaScript中的this指向问题,有时候会让人难以捉摸,随着学习的深入,我们可以逐渐了解
现在我们需要掌握函数内部的this几个特点:

  1. 构造函数在定义的时候this是不确定的,只有在调用的时候才可以确定
  2. 一般函数如果直接执行(即没有用.调),则内部this指向全局window对象. //类似定位父级那个windowview
  3. fn函数如果是作为一个对象的动作方法时,则内部的this被该对象所调用,那么此时this是指向的是当前this所处的函数是被谁调用,就是指向谁的,没有则是指向window(下面案例1和2就是,和this所处的当前作用域无关)
  4. 而构造fn函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来自动调用该构造函数,从而实现实例化对象,此时构造函数内部的this是指向当前作用域所在的对象

先搞明白一个很重要的概念 —— this 的值是在执行的时候才能确认,定义的时候不能确认! 为什么呢 —— 因为 this 是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。看如下例子:

// 情况1
function foo() {
  console.log(this.a) //1
}
var a = 1
foo()

// 情况2
function fn(){
  console.log(this); //输出:Object 
}
var obj={fn:fn};
obj.fn(); //this->obj

// 情况3
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("海牙",18);

//情况4 
var obj = {
    name:'海牙',
    showName:function(){
        console.log(this.name); // 海牙   this => obj
        (function(){ //IIFE,自调用函数,this,全部归为window
            console.log(this.name); //undefined   this=>window ,已经是声明了.
        })();
    }
}

obj.showName();

接下来我们逐一解释上面几种情况

  • 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
  • 对于 obj.foo() 来说,我们只需要记住,谁调用了函数, this指向谁,所以在这个场景下 foo 函数中的 this 就是 obj 对象
  • 在构造函数模式中,类中(函数体中)出现的 this.xxx=xxx 中的 this 是当前类的一个实例
  • IIFE匿名函数自调用时 因为没有明确调用主体 this指向 window

在这里插入图片描述

1.在函数调用的时候 是谁调用的 this指向谁 不看作用域 不看函数是谁的
2.函数内的this指向:看谁调用当前this所在的函数就是指向谁的.
3.谁调用的showName? 谁.showName()? 找不到这个谁的时候 谁=>window
如下是具体案例:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>new this</title>
</head>

<body>
  <script>

    function Cat(name, age) {

      this.name = name;
      this.age = age;
      this.speak = function () {
        console.log('我是' + this.name + '喵喵喵');
      }

    }


    var cat1 = new Cat('橘子', 1);

    cat1.speak();


    /*
    复习:使用new关键字的时候,做了什么事情

    1 创建一个空对象,作为将要返回的对象实例。

    2 将空对象的原型指向了构造函数的prototype属性。

    3 将空对象赋值给构造函数内部的this关键字。

    4 开始执行构造函数内部的代码。


    */
      
     /*fn函数如果是作为一个对象的动作方法时,则内部的this被该对象所调用,那么此时this是指向的是当前this所		处的函数是被谁调用,就是指向谁的,没有则是指向window(下面案例1和2就是,和this所处的当前作用域无关)*/ 
	//案例1:
     var name = '李四';
     var obj = {
     name: '张三',
     speak: function () {
        console.log(this.name);//张三  ,this所在的speak函数是obj去调用的,则this指向obj对象.
        showName(); //window.showName();
      }
    }

    obj.speak(); //obj调用的speak speak 函数内部的this指向obj

     function showName() {
       console.log(this.name); //李四 ,看谁调用当前this所在的函数就是指向谁的.name,没有就是window.
     }

      
    /*
      window.name = '李四';
      对象.方法(); //方法内的this指向 .之前的对象

      函数(); //this => window

    */
      
      
	//案例2:
    var obj1 = {
      name: '张三',
      eat: function () {
        console.log(this.name); //'张三' ,this所在的eat函数是obj1去调用的.
      }
    }

    
    var obj2 = {
      name: '李四',
      eat: function () {
        console.log(this.name); //'李四' ,this所在的eat函数是obj2去调用的.
      }
    }

    obj1.eat(); //
    obj2.eat(); //
         eat(); // 直接报错,没有特定调用则默认是 window.eat();  


      //案例3:
      //无参构造函数写法:
      /*构造fun函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来自动调用该构造函数,从而实现实例化对象,此时构造函数内部的this是指向当前作用域所在的对象*/
	function fun() { 
      this.a = 'mm';  //默认值,不像有参会传进来赋值
      console.log(this.a); // mm   此时this是指向当前
	}
	var obj = new fun(); 
	console.log(obj.a); // mm 
    console.log(this.a);
   // undefined ,为什么? 即不在对象行为方法也不在构造方法内的this指向全局window,this相当于是声明了a,因此undefined
    console.log(a); // 报错
  </script>
</body>

</html>

六.执行上下文栈( 压和和弹栈 遵守先进后出原则)

1.函数多了,就有多个函数执行上下文,每次调用函数创建一个新的执行上下文,那如何管理创建的那么多执行上下文呢?

2.JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则

在这里插入图片描述

从上面的流程图,我们需要记住几个关键点:

  • JavaScript 在执行上是单线程的语言,所有的代码都是 一个一个轮流排队执行的。
  • 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
  • 每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。
  • 浏览器的 JS 执行引擎总是访问栈顶的执行上下文。
  • 全局上下文只有唯一的一个,它在浏览器关闭时出栈。
我们再来看个例子:
var color = "blue";
function changeColor() {
    var anotherColor = "red";
    function swapColors() {
        var tempColor = anotherColor; //color和anothercolor互换位置.
        anotherColor = color;
        color = tempColor;
    }
    swapColors();//
}
changeColor(); //得先调changcolor执行完,才能去执行swapColor,无法跳跃插队.

上述代码运行按照如下步骤:

  • 当上述代码在浏览器中加载时,JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈
  • 调用 changeColor 函数时,此时 changeColor 函数内部代码还未执行,js 执行引擎立即创建一个 changeColor 的执行上下文(简称 EC),然后把这执行上下文压入到执行栈(简称 ECStack)中。
  • 执行 changeColor 函数过程中,调用 swapColors 函数,同样地,swapColors 函数执行之前也创建了一个 swapColors 的执行上下文,并压入到执行栈中。
  • swapColors 函数执行完成,swapColors 函数的执行上下文出栈,并且被销毁。
  • changeColor 函数执行完成,changeColor 函数的执行上下文出栈,并且被销毁。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值