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>