1. 变量提升例子
1.1 eg1
- 形成全局执行上下文EC(G)
- 词法分析,代码执行之前
- 变量提升(当前上下文中带var、function提前声明定义)
- var a; var b; var c; var只声明不定义,全局上下文基于var和function,同时也在window中设置对应属性
- fn = 0x000; 创建堆内存中存放代码串
- 代码执行阶段(变量提升阶段做过的事情,代码执行阶段不重复执行,会忽略)
console.log(a, b, c); // undefined undefined undefined
var a = 12,
b = 13,
c = 14,
function fn(a) { // fn变量提升时候已经声明+定义,代码执行阶段跳过此阶段
/*
私有上下文
初始化作用域链:<EC(fn), EC(G)>
形参赋值:a = 10
变量提升:无var和function
代码执行
*/
console.log(a, b, c); // a私有的10 b和c到EC(G)中找 13 14
a = 100; // 私有 a = 100
c = 200; // 全局 c = 200
console.log(a, b, c); // 100 13 200
// 函数执行完成后无返回值,默认返回undefined
// 当前上下文没有被外部占用,出栈释放
}
b = fn(10); // 先把函数执行,返回结果赋值给b b = undefined
console.log(a, b, c); // 12 undefined 200
1.2 eg2
- 全局上下文下,变量提升
- var i; A=0X000; [[scope]]:EC(G) 作用域是全局
- var y; B=0x001; [[scope]]:EC(G)
- 代码执行 i = 0;
var i = 0;
function A() {
/*
EC(A1)
作用域链<EC(A1), EC(G)>
形参赋值:无
变量提升:var i; x = 0x100 [[scope]]:EC(A1) 作用域是A1
执行 i = 10
*/
var i = 10;
function x() {
console.log(i)
}
return x; // return 0x100
// 0x100被全局的y占用,不能被释放,闭包,被保留
}
var y = A(); // A()的返回结果给y = 0x100
y(); // 10
function B() {
var i = 20;
y();
}
B(); // 10
函数执行,它的上级作用域(上下文)是谁,和函数在哪执行是没有关系的,只和在哪创建有关系,在哪创建的,它的[[scope]]就是谁,也就是它的上下文就是谁
1.3 eg3
- 变量提升:
- var a;
- var obj;
- fn = 0x000; [[scope]]: EC(G) 不管fn在哪执行,fn的上级上下文永远是全局作用域
- 代码执行
var a = 1;
var obj = { // 0bj = 0x001 对象开堆内存
name: 'tom'
};
function fn() {
/*
fn执行形成私有上下文
作用域链 <EC(fn), EC(G)>
形参赋值:无
变量提升:var a2; // 只是有a2是私有的, a/obj2/obj都是全局的
代码执行
没有被占用,释放
*/
var a2 = a; // a2 = 1
obj2 = obj; // 当前上下文,全局都没有0bj2,给winndow加了obj2,obj2 = 0x001
a2 = a; // a2 = 1
obj2.name = 'jack'; // 0x001.name = 'jack'
}
fn(); // fn执行
console.log(a); // 1
console.log(obj); // {name: 'jack'}
1.4 eg4
- 变量提升
- var a;
- fn = 0x000; [[scope]]:EC(G)
- 代码执行
var a = 1; // a = 1
function fn(a) {
/*
EC(fn)
初始化作用域链<EC(Fn), EC(G)>
形参赋值 a = 1
变量提升
var a; (已经有形参a,a变量已经存在AO(Fn)此提升会忽略)
a = 0x001; [[scope]]:EC(fn) 不会重新声明a, 但是会重新赋值
代码执行
*/
console.log(a) // 0x001
var a = 2; // 重新赋值
console.log(a) // 2
function a() {} // 变量提升已经处理过,代码执行跳过
console.log(a) // 2
}
fn(a); // 执行fn(1)
console.log(a) // 1 全局的a
1.5 eg5
1.5.1 情况1
- 变量提升:
- var a;
- fn = 0x000; [[scope]]:EC(G)
- 代码执行
console.log(a); // undefined
var a = 12;
function fn() {
/*
fn执行
初始化作用域链<EC(FN), EC(G)>
形参赋值:无
变量提升: var a; //私有 a
*/
console.log(a); // undefined
var a = 13; // 私有a = 13
}
fn(); // 执行
console.log(a); // 12
1.5.2 情况2
- 变量提升:
- var a;
- fn = 0x000; [[scope]]:EC(G)
- 代码执行
console.log(a); // undefined
var a = 12;
function fn() {
/*
fn执行
初始化作用域链<EC(FN), EC(G)>
形参赋值:无
变量提升:无 没有私有变量
*/
console.log(a); // 12
a = 13; // 全局a = 13
}
fn(); // 执行
console.log(a); // 13
1.5.3 情况3
- 变量提升:
- fn = 0x000; [[scope]]:EC(G)
- 代码执行
console.log(a); // 输出变量首先看是否是自己的私有变量,不是自己私有则找上级,都没有 a is not defined 代码中断执行
a = 12;
function fn() {
console.log(a); // 12
a = 13; // 全局a = 13
}
fn(); // 执行
console.log(a); // 13
1.6 eg6
- 变量提升:
- var foo;
- 自执行函数,不参与变量提升,代码运行到才创建
var foo = 'hello';
(
function (foo) {
/*
EC(any)
初始化作用域链<EC(any), EC(G)>
形参赋值:foo = 'hello'
变量提升: var foo, 当前上下文有foo,忽略此步声明
代码执行
*/
console.log(foo); // hello
var foo = foo || 'world'; // foo = 'hello'
console.log(foo); // 'hello'
}
)(foo); // 自执行函数执行。传递实参 'hello'
console.log(foo); // hello
|| 和 &&- A||B:A为真返回A的值,A为假返回B的值,
- A&&B:A为真返回B的值,A为假,返回A的值
- ||和&&同时出现的时候,&&的优先级高于||
1.7 eg7
//
{
function foo() {}
foo = 1;
}
console.log(foo);
// ---------------
{
function foo() {}
foo = 1;
function foo() {}
}
console.log(foo);
// ---------------
{
function foo() {}
foo = 1;
function foo() {}
foo = 2;
}
console.log(foo);
2. 数据类型
2.1 把一个对象数据类型的值,转换为数字/字符串
-
把一个
对象数据类型的值,转换为数字/字符串- 首先查找对象的
Symbol.toPrimitive属性 - 如果没有这个属性,紧接着调用对象的
valueOf方法来获取原始值(基本类型值) - 如果获取不到原始值,则再调用
toString转化为字符串或者Number转化为数字
- 首先查找对象的
-
场景
- 在
+运算中,如果左右两边出现字符串(或者是部分对象值),则不是数学运算,会变成字符串拼接 - alert会把值,隐式转换为字符串输出
- 字符串拼接,模板字符串中实现的是字符串拼接,遇到对象会转换成字符串
- 其余的数学运算中,例如
- / * %中会把对象转换成数字 ==比较中,也会把对象转换为字符串或者数字
- 在
let obj = {
name: 'this is a obj'
}
let arr = [10, 20, 30]
let time = new Date()
let num = new Number(10)
console.log(obj, arr, time, num)
console.log(10 + obj) // '10[object, Object]'
// 1. obj[Symbol.toPrimitive] => undefined
// 2. obj.valueOf() => {name: 'xxx'} 不是原始值,原始值是基本类型
// 3. obj.toString() => '[object, Object]'
console.log(10 + num) // 20
// 1. num[Symbol.toPrimitive] => undefined
// 2. obj.valueOf() => 10 是原始值,原始值是基本类型
obj[Symbol.toPrimitive] = function() {
return 100;
}
console.log(10 + obj) // 110
// 默认参数hint
obj[Symbol.toPrimitive] = function(hint) {
console.log(hint) // => default或者number或者string 类型
return 100;
}