尚硅谷JavaScript高级教程(javascript实战进阶)

01 - 10

准备

  • 开课前的一些基础知识(略)

数据类型

分类:
基本数据类型(值)
① String - 任意字符串 ② Number - 任意的数字 ③ boolean - true/false
④ undefined - undefined ⑤ null - null
引用数据类型(对象)
① Object - 任意对象 ② Function - 一种特别的对象(可以执行)③ Array - 一种特别的对象(数值下标, 内部数据是有序的)

判断数据类型
typeof

  • 可以判断undefined/数值/字符串/布尔值/function
  • 不能判断:null与object object与array

instanceof

  • 判断对象的具体类型(!!!)

===(尽量用三个等号)

  • 可以判断 undefined,null
<script>
    // 1、基本数据类型
    // typeof 返回数据类型的字符串表达
    var a;
    console.log(a, typeof a, typeof a === 'undefined', a === undefined); // undefined 'undefined' true true
    console.log(undefined === 'undefined'); // false

    a = 3;
    console.log(typeof a === 'number'); // true

    a = 'atguigu';
    console.log(typeof a === 'string'); // true  string小写

    a = true;
    console.log(typeof a === 'boolean'); // true
    
    a = null;
    console.log(typeof a, a === null); // object true
    console.log("--------------------");

    // 2、对象数据类型
    var b1 = {
        b2:[1, 'abc', console.log],
        b3:function(){
            console.log('b3');
            return function(){
                return 'xfzhang';
            }
        }
    };
    console.log(b1 instanceof Object, b1 instanceof Array); // true false
    console.log(b1.b2 instanceof Array, b1.b2 instanceof Object); // true true
    console.log(b1.b3 instanceof Function, b1.b3 instanceof Object); // true true

    // 判断是否是函数还有一种方式
    console.log(typeof b1.b3 === 'function'); // true
    // new Object();  Object - 其实就是一个构造函数

    console.log(typeof(b1.b2[2]) === 'function'); // 后面可以加上括号的,前面肯定是一个函数
    // 不要拘泥于表面,要观察自己得到的是哪一类型的数据
    console.log(b1.b3()()); // xfzhang

    console.log(typeof b1.b2); // object 也就是不能判断object和array
    b1.b3(); // b3
</script> 

相关问题

实例:实例对象
类型:类型对象

1、undefined 与 null的区别

  • undefined 代表定义未赋值
  • null 代表定义并赋值了,只是值为null

2、什么时候给变量赋值为null呢?

  • 初始赋值,表明将要赋值为对象
  • 结束前,让对象称为垃圾对象(被垃圾回收器回收)

3、严格区分变量类型和数据类型?

  • 数据的类型:① 基本类型 ② 对象类型(一般对象类型就是引用类型)
  • 变量的类型(变量内存值的类型): ① 基本类型:保存的就是基本类型的数据 ② 引用类型:保存的是地址值
<script>
    /* 
        实例:实例对象
        类型:类型对象

     */
    function Person(name, age) { // 构造函数 类型
        this.name = name;
        this.age = age;
    }
    var p = new Person('tom', 12); // 根据类型创建的实例对象

    /* 
        1、undefined 与 null的区别
           undefined 代表定义未赋值
           null 代表并赋值了,只是值为null
        2、什么时候给变量赋值为null呢?
            初始赋值,表明将要赋值为对象
            结束前,让对象称为垃圾对象(被垃圾回收器回收)
        3、严格区分变量类型和数据类型?
            数据的类型
                基本类型
                对象类型(一般对象类型就是引用类型)
            变量的类型(变量内存值的类型)
                基本类型:保存的就是基本类型的数据
                引用类型:保存的是地址值
     */
    //  1、undefined 与 null的区别
    var a;
    console.log(a); // undefined
    a = null;
    console.log(a); // null

    /* 
        初始赋值为null,表明将要赋值为对象

        对象有个概念是垃圾对象,即没有对象指向它
     */
    var b = null;
    // 确定对象就赋值
    b = ["atguigu", 12];
    // 最后,让b指向的对象称为垃圾对象(被垃圾回收器回收)
    b = null;

    // var c = {};

    var c = function(){
    };
    console.log(typeof c); // 'function' 其实是根据这个地址值来去堆内存中查找,然后发现其是一个函数类型
</script>

内存

1、什么是数据?

  • 存储在内存中代表特定信息的“东东”,本质上是0101…
  • 数据的特点:可传递,可运算
  • 一切皆数据
  • 内存中所有操作的目标:数据 -> ① 算数运算 ② 逻辑运算 ③ 赋值运算

2、什么是内存?

  • 内存条通电以后产生的可存储数据的空间(临时的)
  • 内存产生和死亡:内存条(电路版)==> 通电 ==> 产生内存空间 ==> 存储数据 ==> 处理数据 ==> 断电 ==> 内存空间和数据都消失
  • 一块小内存的2个数据? ① 内存存储的数据 ② 地址值
  • 内存的分类:① 栈:全局变量和局部变量(函数名 - 本身就是一个变量名)② 堆:对象(函数是一个对象)

3、什么是变量?

  • 可变化的量,由变量名和变量值组成
  • 每个变量都对应的一块小内存,变量名用来查找到对应的内存,变量值就是内存中保留的数据

4、内存,数据,变量三者之间的关系

  • 内存用来存储数据的空间
  • 变量是内存的标识

数据的特点:可传递,可运算

var age = 18;
var obj = {name:'tom'};
var a = obj; // 将obj里面的数据拷贝一份到a中,
console.log(obj.name); // 

/* 
    数据的特点:可传递,可运算
*/
var a = 3;
var b = a;
var c = a + 2;

相关问题1

1、var a = xxx, a内存中到底保存的是什么?

  • xxx是基本数据,保存的就是这个数据
  • xxx是一个对象,保存的是对象的地址值,
  • xxx是一个变量,保存的是xxx的内存内容(可能是基本数据,也可能是地址值)
var a = 3;
a = function () {

}
var b = 'abc';
a = b;
b = {};
a = b;

关于引用变量赋值问题?

  • n个引用变量指向同一个对象,通过一个变量修改对象内部数据,其他所有变量看到的是修改之后的数据
  • 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象
//  n个引用变量指向同一个对象,通过一个变量修改对象内部数据,其他所有变量看到的是修改之后的数据
var obj1 = { name: 'tom' };
var obj2 = obj1; // 将obj1中的内存内容保存到obj2中,只是这个内容是地址值
obj1.age = 12;
console.log(obj1.age); // 12

function fn(obj) {
    obj.name = 'Bob';
}
fn(obj1);
console.log(obj2.name); // Bob
// 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象
var a = { age: 12 };
var b = a;
// 后面a指向的对象进行了相应的修改
a = { name: 'Bob', age: 13 };
console.log(b.age, a.name, a.age); // 12 Bob 13

function fn2(obj) {
    obj = { age: 15 }; // 这里的obj和a是完全不同的两个对象

}
fn2(a); // 将a的内容赋值给obj,只是这里是地址值,既然是地址值就应该指向同一个对象
console.log(a.age); // 13  15确实在obj中,

相关问题2

1、在js调用函数时,传递变量参数时,是值传递还是引用传递?

  • 理解1:都是值(基本值/地址值)传递,
  • 理解2:可能是值传递,也可能是引用传递(地址值);上面理解1,2中括号是重点
var a = 3;
function fn(a) { // 新的变量 操作的是3这个数据,而不是a,这行的a和上面的a没有绝对的关系
    a = a + 1; // 对小内存时候,对小内存操作不是读就是写,
    console.log(a);
};
fn(a); // 4
console.log(a); // 3

function fn2(obj) {
    console.log(obj.name);
}
var obj = { name: 'Tom' };
fn2(obj); // 传的是obj的内容,只是这个内容是地址值 Tom

2、JS引擎中如何管理内存?

一、内存生命周期?

  • 分配小内存空间 -> 得到它的使用权
  • 存储数据,可以反复进行操作
  • 释放空间

二、释放内存

  • 局部变量:函数执行完自动释放
  • 对象:成为垃圾对象 ==> 垃圾回收器回收
var c = 3;
var obj3 = {};

function fn3() {
    var d = {};
};
fn3(); // d是自动释放,d所指向的对象是在后面的某个时刻由垃圾回收器回收

对象

1、什么是对象?

  • 多个数据的封装体
  • 用来保存多个数据的容器
  • 一个对象代表现实中的一个事物

2、为什么要用对象?

  • 统一管理多个数据

3、对象的组成?

  • 属性:属性名(字符串)和属性值(任意类型)组成,
  • 方法:一种特别的属性(属性值是函数)

4、如何访问对象内部数据?

  • .属性名 :编码简单,有时不能用 --> 下面具体说明
  • [‘属性名’]:编码麻烦,但是能通用
var p = {
    name: 'Tom',
    age: 12,
    setName: function (name) {
        this.name = name;
    },
    setAge: function (age) {
        this.age = age;
    }
}
// console.log(p.name, p.setName);
p.setName('Bob'); // 传入了一个参数
p['setAge'](23);
console.log(p.name, p['age']); // Bob 23

什么情况下必须使用[‘属性名’]的方式? 一般不用

  • 属性名包含特殊字符: - 空格
  • 属性名不确定 -> 通过变量来存
var q = {};
// 1、给q对象添加一个属性,content-type:text/json
// - 属性名包含特殊字符: - 空格
q['content-type'] = 'text/json';
console.log(q['content-type']);

// 2、变量名不确定
var propName = 'myAge';
var value = 18;
// q.propName = value; 不能用
q[propName] = value;
console.log(q[propName]); // 18

函数

1.什么是函数?

  • 实现特定功能的n条语句的封装体
  • 有函数是可以执行的,其他类型的数据不能执行

2.为什么要用函数?(函数就是一种封装的思想)

  • 提高代码复用
  • 便于阅读交流

3.如何定义函数?

  • 函数声明
  • 表达式

4.如何调用(执行)函数?

  • test() :直接调用
  • obj.test() : 通过对象调用
  • new test() : new调用
  • test.call/apply(obj); : 相当于obj.test():临时让test成为obj的方法进行调用

函数小例子

/*
   编写程序实现以下功能需求:
        1.根据年龄输出对应的信息
        2.如果小于18,输出:未成年,再等等
        3.如果大于68,输出:算了吧!
        4.其它,输出:刚好!
*/
// 是否需要参数是看有没有哪个内容是变化的
function showInfo(age) {
    if(age < 18){
        console.log("未成年,再等等");
    }else if(age > 60){
        console.log("算了吧");
    }else{
        console.log("刚好");
    }
};
showInfo(17); // 未成年,再等等
  • 定义函数的两种方式
function fn1(){ // 函数声明
    console.log("fn1()");
};
var fn2 = function(){ // 表达式
    console.log("fn2()");
};
fn1();
fn2();
  • test.call/apply(obj); : 相当于obj.test():临时让test成为obj的方法进行调用
var obj = {};
function test2(){
    this.xxx = 'atguigu';
};
// 报错
obj.test2(); // Uncaught TypeError: obj.test2 is not a function
// 可以让一个函数称为任意对象的方法进行调用
test2.call(obj); // obj去调用的test2  obj.test2()  --> 可以让一个函数成为指定任意对象的方法进行调用
console.log(obj.xxx);

回调函数

1、什么函数才是回调函数?(下面有两个例子)

  • 你定义的
  • 你没有调用
  • 但最终它执行了

2、常见的回调函数?

  • dom事件回调函数
  • 定时器回调函数
  • ajax请求回调函数(后面讲)
  • 生命周期回调函数(后面讲)

3、前端

  • 画(主要是css)主要是浏览器
  • 交互()
<body>
    <button id="btn">测试点击事件</button>
    <script>
        // dom事件回调函数,不用返回获取的对象直接进行调用方法即可
        document.getElementById("btn").onclick = function(){
            alert(this.innerHTML);
        };
        // 定时器回调函数
        // 定时器:① 超时定时器,② 循环定时器
        setTimeout(function(){
            alert("到点了");
        }, 2000);
        
    </script>
</body>

IIFE

1、理解:IIFE?

  • 全称:ImmediateLy-Invoked Function Expression
  • 其实就是匿名函数自调用

2、作用?

  • 隐藏实现
  • 不会污染外部(全局)命名空间
  • 用它来编写js模块(模块的概念后面会讲)
(function () { // 匿名函数自调用
    var a = 3;
    console.log(a + 3); // 6
})();
var a = 4;
console.log(a); // 4

(function () {
    var a = 1;
    function test() {
        console.log(++a);

    };
    /* function test2() {
        console.log('test2()');
    } */
    window.$ = function () { // 向外暴露一个全局变量
        return {
            test: test
        }
    };
})();

$().test(); // 2  $是一个函数名,$执行后返回的是一个对象

函数中的this

1、this是什么?

  • 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是Window
  • 所有函数内部都有一个变量this:它的值是调用函数的当前对象

2、如何确定this的值?

  • . test(): window
  • p.test: p
  • new test(): 新建对象
  • p.call(obj): obj
<script>
    function Person(color) {
        // this代表的是谁来调用Person
        console.log(this);
        this.color = color;
        this.getColor = function () {
            console.log(this);
            return this.color; 
        };
        this.setColor = function(color){
            console.log(this);
            this.color = color;
        };
    };
    Person("red"); // this是谁?  Window
    
    var p = new Person("yellow"); // this是谁? p 让当前new的对象来调用的

    p.getColor(); // this是谁?  p

    var obj = {};
    // 让setColor成为obj临时的方法进行调用
    p.setColor.call(obj, "black"); // this是谁? obj

    var test = p.setColor;
    test(); // this是谁? Window 因为没有指定

    function fun1(){
        function fun2(){
            console.log(this);
        };
        fun2(); // this是谁? Window
    };
    fun1();
</script>

关于语句分号问题

  • js编程中每条语句后面是否加分号,看个人习惯。

3.在下面2种情况下不加分号会有问题!

  • 小括号开头的前一条语句
  • 中方括号开头的前一条语句
<script>
    /* 
        1.js一条语句的后面可以不加分号
        2.是否加分号是编码风格问题没有应该不应该,只有你自己喜欢不喜欢
        3.在下面2种情况下不加分号会有问题
            - 小括号开头的前一条语句
            - 中方括号开头的前一条语句
        4.解决办法:在行首加分号
        5.强有力的例子:vue.js库
     */
    // 小括号开头的前一条语句
    var a = 3
    ; (function () {

    })();
    /* 
        错误理解:
        var a = 3(function () {

        })();
    */  

    var b = 4
    ;[1, 3].forEach(function(){

    });
    /* 
        错误理解:
        var b = 4[3].forEach(function(){

        });
     */
</script>

webstorm设置

略;一般企业现在都是使用VScode进行编码。

复习

  • 见思维导图,无非就是前面讲到的那些内容。

函数的prototype

1.函数的prototype.属性(图)

  • 每个函数都有一个prototype.属性,它默认指向一个Object.空对象(即称为:原型对象)
  • 原型对象中有一个属性constructor,它指向函数对象

2.给原型对象添加属性(一般都是方法)

  • 作用:函数的所有实例对象自动拥有原型中的属性(方法)
<script>
    console.log(Date.prototype, typeof Date.prototype); // {} 但是对于Date来讲,里面就包含了很多方法,'object'

    function Fun() { // alt + shift + r(重命名 rename)
     
    };
    console.log(Fun.prototype); // 它默认指向一个Object.空对象(没有我们的属性)

    /* Fun.prototype.test = function () {
        console.log('test()');
    }; // 本来是一个空对象,现在已经添加了test() 方法, */ 

    // 原型对象中有一个属性constructor,它指向函数对象
    // 构造函数和它的原型对象是相互引用的关系,里面有属性可以相互引用
    console.log(Date.prototype.constructor === Date); // true
    console.log(Fun.prototype.constructor === Fun); // true

    // 给原型对象添加属性(一般是方法) ===> 实例对象可以访问
    Fun.prototype.test = function () {
        console.log('test()');
    }; // 本来是一个空对象,现在已经添加了test() 方法, 

    var fun = new Fun();
    fun.test(); // 'test' 进行相应的输出了
</script>
  • 对于代码而言需要画出其中的内存结构图才好
    在这里插入图片描述

显式原型和隐式原型

  • 每个函数function都有一个prototype,即显式原型(属性)
  • 每个实例对象都有一个__proto__,可称为隐式原型(属性)
  • 实例对象的隐式原型的 值为其对应构造函数的显式原型的值,- 即都指向Object

总结:

  • 函数的prototype属性:在定义函数时自动添加的,默认值是一个空0bject对象
  • 对象的__proto__属性:创建实例对象时自动添加的,默认值为构造函数的prototype属性值(隐式)
  • 程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前) ESMA专门用来制定JS规范的一个组织
/* 
    函数名在栈空间里面,函数对象是在堆空间里面
    {} 也是一个对象 
*/
// 一、定义构造函数
function Fn() { // 内部语句: this.prototype = {} 

}
/* 
    Fn.prototype和fn.__proto__一样,里面保存的都是地址值
*/
// 1、每个函数function都有一个prototype,即显式原型(属性)- 默认指向一个空的Object对象
console.log(Fn.prototype) // 显式原型  引用变量属性,里面放的是地址值

// 2、每个实例对象都有一个__proto__,可称为隐式原型(属性)
// 二、创建实例对象
var fn = new Fn() // 内部语句: fn.__proto__ = Fn.prototype (内部语句也是要画出来的)
console.log(fn.__proto__) // 隐式原型,为了测试

// 3、对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__) // true

// 三、给原型添加方法
Fn.prototype.test = function () {
    console.log('test()')
}

// 通过实例对象调用原型的方法
fn.test() // test()

/* 
    作业必须完成内存图的画,下面这个图和上面的图其实是一样的
 */

在这里插入图片描述

原型链

1.原型链(图解)
访问一个对象的属性时

  • 先在自身属性中查找,找到返回
  • 如果没有,再沿着__proto__这条链向上查找,找到返回
  • 如果最终没找到,返回undefined

别名:隐式原型链(其实根本就不看显式的); 作用:查找对象的属性(方法)

console.log(Object) // 一上来就有,而且是全局的
// console.log(Object.prototype)
console.log(Object.prototype.__proto__) // null
function Fn(){
    this.test1 = function(){
        console.log('test1()')
    }
}
console.log(Fn.prototype)

Fn.prototype.test2 = function(){
    console.log('test2()')
}
// 创建一个实例对象
var fn = new Fn()
fn.test1()
/* 
    对于实例对象,现在自身里面查找,如果有则ok
    没有的话根据隐式原型属性找原型对象,查是否有,
 */
fn.test2()
console.log(fn.toString())

console.log(fn.test3) // undefined
fn.test3() // 报错:fn.test3 is not a function

内存结构图
在这里插入图片描述
注意点:

  • 查找变量使用的是作用域;函数对象有一个显示原型属性,默认是一个空的object对象
  • 函数也是一个实例对象,函数是Function的实例对象
  • 隐式原型会指向显式原型,具体来说它们是指向同一个对象
  • 实例对象才有隐式原型属性,构造函数的显示;实例对象的隐式原型等于构造函数的显示原型 --> 具体来说是它们指向同一个东西
  • Object() 是Function的实例,所有函数对象的隐式原型都指向Function的显式原型
  • 原型中的构造函数的实例和方法是给实例用的

构造函数/原型/实例对象的关系1(图解)

var o1 = new Object();
var o2 = {};

在这里插入图片描述
构造函数/原型/实例对象的关系2(图解)

function Foo(){  }
// 上面等价于
var Foo = new Function()
// 而可以推导出
Function = new Function()
// 所有函数的__proto__都是一样的,因为他们都是
new Function() 产生的
  • 而一般创建出来的是Object的

在这里插入图片描述

  • 所有的函数对象都是Function的实例,所以才会有那一根线
  • 所有函数都有两个属性,一个显式原型,一个隐式原型
  • 也就是说所有函数对象都有一个隐式原型属性指向大写Function的显式原型
  • 实例对象的隐式原型属性等于构造函数的显式原型属性
  • 所有函数的隐式原型都应该相等,都是大写Function的显式原型对象

Object是Function的一个实例 + 实例对象的隐式原型属性等于构造函数的显式原型属性

可以得到下面这种图
在这里插入图片描述
构造函数的实例对象自动拥有构造函数原型对象的属性(方法) ==> 原型上的属性和方法给实例用 — 利用的就是原型链

原型链补充

  • 函数的显示原型指向的对象,默认是空Object实例对象

原型链

  • 访问一个对象的属性时:先在自身属性中查找,找到返回 --> 如果没有,再沿着__proto__这条链向上查找,找到返回 --> 如果最终没找到,返回undefined
  • 别名:隐式原型链(其实根本就不看显式的)
  • 作用:查找对象的属性(方法)
  • 构造函数/原型/实体对象的关系(图解)
  • 构造函数/原型/实体对象的关系2(图解)
console.log(Object) // 一上来就有,而且是全局的
// console.log(Object.prototype)
console.log(Object.prototype.__proto__) // null
function Fn() {
    this.test1 = function () {
        console.log('test1()')
    }
}
console.log(Fn.prototype)

Fn.prototype.test2 = function () {
    console.log('test2()')
}
// 创建一个实例对象
var fn = new Fn()
fn.test1()
/* 
    对于实例对象,先在自身里面查找,如果有则ok
    没有的话根据隐式原型属性找原型对象,查是否有,
 */
fn.test2()
console.log(fn.toString())

console.log(fn.test3) // undefined
// fn.test3() // 报错:fn.test3 is not a function


/* 
    1、函数的显示原型指向的对象:默认是空Object实例对象
        - 但Object不满足,
    2、
 */
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true

/* 
    2、所有函数都是Function的实例(包含Function本身)

*/
console.log(Function.__proto__ === Function.prototype) // true

//  3、Object的原型对象是原型链尽头
console.log(Object.prototype.__proto__) // null
  • 理解的话只能通过图来进行相应解释才行

原型链 - 属性问题

  • 读取对象的属性值时:会自动到原型链中查找
  • 设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
  • 方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
function Fn() {

}
Fn.prototype.a = 'xxx'
// 原型对象上的属性,实例对象都是可见的
var fn1 = new Fn()
console.log(fn1.a) // xxx

var fn2 = new Fn()
// 如果当前对象中没有此属性,直接添加此属性并设置其值
// 设置了一个新的对象,当该对象没有属性值的时候,直接添加属性a,并设置其值即可

fn2.a = 'yyy'
console.log(fn1.a) // xxx
console.log(fn2.a)

// 方法一般放在原型中
Person.prototype.setName = function (name) {
   this.name = name
}
// 属性一般放在每个对象上
function Person(name, age) {
   this.name = name
   this.age = age
}

var p1 = new Person('Tom', 12)
p1.setName('Bob')
// console.log(p1.name)
console.log(p1)

var p2 = new Person('Jack', 12)
p2.setName('Cat')
console.log(p2)

console.log(p1.__proto__ === p2.__proto__) // true

探索instanceof

1.instanceof是如何判断的?

  • 表达式:A instanceof B
  • B是一个函数有显式原型属性,而A是实例对象有隐式原型
  • 如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false,在原型链上
  • Function,是通过new自己产生的实例
/*
案例一
*/
function Foo() {

}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true

/* 
案例二
*/
console.log(Object instanceof Function); // true
// 右边那条线 --> 函数的原型对象默认是空的Object的实例对象
console.log(Object instanceof Object); // true
// Function是new自己产生的
console.log(Function instanceof Function); // true

console.log(Function instanceof Object); // true
function Foo(){

}
console.log(Object instanceof Foo) // false

变量提升与函数提升

<script>
    /* 
    1.变量声明提升
    *通过var定义(声明)的变量,在定义语句之前就可以访问到
    *值:undefined,只是当时的值是undefined
    2.函数声明提升 
    *通过Function声明的函数,在之前就可以直接调用
    *值:函数定义(对象)

    不管是变量声明提升还是函数声明提升都是一种结果
    3.问题:变量提升和函数提升是如何产生的?
    */
    console.log('-------------')
    var a = 3
    function fn() {
        /* console.log(a)
        var a = 4 */
        // 相当于
        var a
        console.log(a) 
        a = 4
    }
    fn() // undefined

    console.log(b) // undefined 变量提升
    fn2() // 可调用,( fn2() )函数提升
    fn3() // 不能,变量提升
    var b = 3

    function fn2() {
        console.log('fn2()')
    }

    var fn3 = function () {
        console.log('fn3()')
    }

    fn3()  // 因为前面报错了,所以不会执行

</script>
  • 程序在报错以后,后面的代码一般不执行

执行上下文

代码分类(位置)

  • 全局代码
  • 函数(局部代码)

arguments

  • 伪数组,将传入的实际的参数,传入到一个数组当中用来存储。
<script>
/* 
1.代码分类(位置)
*全局代码
*函数(局部)代码
2.全局执行上下文
*在执行全局代码前将Window确定为全局执行上下文,
*对全局数据进行  预处理 (重要的就是预处理操作)
    *var定义的全局变量=>undefined,添加为window的属性
    *function声明的全局函数==>赋值(fun),添加为window的方法
    *this==>赋值(window)
*开始执行全局代码
3.函数执行上下文
*在调用函数时,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
*对局部数据进行预处理
    *形参变量=>赋值(实参)==>添加为执行上下文的属性
    *arguments=:>赋值(实参列表),添加为执行上下文的属性
    *var定义的局部变量=>undefined,.添加为执行上下文的属性
    *function声明的函数=>赋值(fun),添加为执行上下文的方法
    *this==>赋值(调用函数的对象)

    开始执行函数体代码

    内存空间是隔离的,变量中你在你的区域,我在我的区域,互相都没有被打扰
*/
// 全局执行上下文 window
console.log(a1, window.a1)
console.log(a2)
// 使用这种方式是可以直接调用的
a2() // 先去函数中查找,然后去window中查找
window.a2()
console.log(this) // Window

var a1 = 3
function a2() {
    console.log(a2)
}
console.log("-----------------")

// 函数执行上下文
function fn(a1) {
    console.log(a1) // 2
    console.log(a2) // undefined
    // console.log(a3)
    a3() // a3()

    console.log(this) // window
    console.log(arguments) //  伪数组

    var a2 = 3
    function a3() {
        console.log('a3()')
    }
}
// 函数执行前,需要全部创建好即可
fn(2, 3)
</script>

执行上下文栈

  • 采用的是n+1原则,n指的是有几次函数的调用,1指的是window,切记有可能调用好几次且调用的函数里面还有函数记得算进去
<script>
    /* 
        1.在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
        2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
        3.在函数执行上下文创建后,将其添加到栈中(压栈)
        4.在当前函数执行完后,将栈顶的对象移除(出栈)
        5.当所有的代码执行完后,栈中只剩下window
    */
    // n + 1  n 指的是有几次函数的调用,切记有可能调用好几次且调用的函数里面还有函数记得算进去
    var a = 10
    var bar = function (x) {
        var b = 5;
        // 调用一次函数产生一对函数的执行上下文
        foo(x + b) 
    }
    // 赋值语句执行了,并不代表函数执行了
    var foo = function (y) {
        var c = 5
        console.log(a + c + y) // 30
    }
    /* 
        定义的时候不产生函数上下文对象,调用的时候会产生函数上下文对象

        全局上下文总是在最底部

        一个函数执行完,就需要移除,除非是该函数再调用其他函数 -- 针对函数调用栈来讲的
    */
    bar(10)
</script>
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值