JavaScript的核心

一、对象

1. 对象属性

let obj = {},
	a = 0,
	b = '0';


obj[a] = 'hello';
obj[b] = 'world';


console.log(obj[a]); //=>world
let obj = {},
	a = Symbol(1),
	b = Symbol(1);


obj[a] = 'hello';
obj[b] = 'world';


console.log(obj[a]); //=>hello
let obj = {},
	a = {name: 'hello'},
	b = {name: 'world'};


obj[a] = 'hello';
obj[b] = 'world';


console.log(obj[a]); //=>world

分析:

  • 对象的属性可以是任何类型,但这不意味对象的键就是该类型,如果对象的键不是一个字符串时,那么最终浏览器会将其转换为 字符串 存储。
  • Symbol定义的变量时 唯一 标识。
  • 对象转为字符串的结果实际上都是 一致("[object Object]")
    , 前提该对象没有valueOf。

2. 对象的浅克隆

① Object.assign

let obj1 = {
	a: 1,
	b: {
		a: 22,
	},
	c: /^\d{11}$/,
	d: new Date()
}

let obj3 = {};
let obj2 = Object.assign(obj3, obj1);
console.log(obj2 === obj3); // => true

分析:

  • Object.assign()方法返回 第一个 参数,也即parameter1===return。
  • Object.assign()方法只是一个浅克隆。

② 循环遍历

function copyLight(obj){
	if(obj === null) return null;
	let obj1 = {};
	for(let key in obj){
		// 判断是否为私有的属性
		if(!obj.hasOwnProperty(key)) break;
		obj1[key] = obj[key];
	}
	return obj1;
}


let obj2 = copyLight(obj1);
console.log(obj2);

3. 对象的深拷贝

① JSON.stringify()

let obj1 = {
	a: 1,
	b: {
		a: {
			a: 1,
		},
	},
	c: /^\d{11}$/,
	d: new Date(),
	e: function(){console.log('a')}
}


// 方法一
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2);

存在问题:

  • 对于正则表达式,最终是一个 空对象
  • 拷贝的 时间不一致
  • 对于函数属性,会直接 丢失

② 循环

function copyDeep(obj){
	if(obj === null) return null;
	if(typeof obj != 'object') return obj;
	if(obj instanceof RegExp) return new RegExp(obj);
	if(obj instanceof Date) return new Date(obj);

	// 防止用户修改obj的constructor,导致最终的类型不一致
	let obj1 = new obj.constructor;
	for(let key in obj){
		if(obj.hasOwnProperty(key)){
			obj1[key] = copyDeep(obj[key]);
		}
	}

	return obj1;
}

let obj2 = copyDeep(obj1);
console.log(obj2);

思路:

  • 使用递归的操作,我们在函数执行最开始进行 所有的情况 的分析,最终可以直接减少for循环中的检测。
  • 我们必须有 返回值,否则赋值无效。

二、作用域

1. 块级作用域

let a = [];

for(var i = 0; i < 10; i++) {
	a[i] = function() {
		console.log(i);
	}
}

a[6](); // =>10
let a = [];

for(let i = 0; i < 10; i++) {
	a[i] = function() {
		console.log(i);
	}
}

a[6](); // =>6

注意:

  • 在for循环中使用var,不存在块级作用域,即此时var定义的变量为可被 全局 访问。
  • 使用let存在 块级作用域

三、闭包

1.

let a = 0,
	b = 0;


function fun(a) {
	fun = function(b) {
		console.log(a + b++);
	}
	console.log(a++);
}


fun(1); // =>1 
fun(2); // =>4

注意:

  • ++运算符。
  • 函数内部可以读写函数。
  • 闭包有变量 保存 的作用。

四、预解析

1. 函数提升

var a = function() {
	console.log('a1');
}


function a() {
	console.log('a2');
}


a(); // => a1

分析代码执行:

/* 函数提升 */
var a; // a=>undefined
function a(){...a2} // a=>function(){...a2}


/* 函数执行 */
a = function(){...a1} // 1=>function(){...a1}
a() // =>a1

2. 变量和函数提升

var a = 1;

function a() {
	console.log('a2');
}

console.log(a); //a=>1
  • 代码执行步骤同上

3. 函数中的变量提升

var a = 1;


function fun() {
	console.log(a);
	var a = 12;
}


console.log(a); // => 1
fun(); // => undefined
var a = 1;

function fun() {
	a = 6;
	console.log(a);
	var a = 12;
}

console.log(a); // => 1
fun(); // => 6
console.log(a); // => 1

注意:

  • 函数中使用var申明的变量也会产生变量提升。
  • 函数中的变量提升,不会被放在全局作用域中的最顶端,而实放在了 函数作用域 的最顶部。

五、函数

1. 函数执行的优先级

function Foo() {
	getName = function() {
		console.log(1);
	}
	return this;
}
Foo.getName = function() {
	console.log(2);
}
Foo.prototype.getName = function() {
	console.log(3);
}

var getName = function() {
	console.log(4);
}

function getName() {
	console.log(5);
}

Foo.getName(); // =>2
getName(); // =>4
Foo().getName(); // =>1
getName(); // =>1
new Foo.getName(); // =>2
new Foo().getName(); // =>3
new new Foo().getName(); // =>3

分析:

  • 函数中的this指向他的 调用者
  • 函数内部可以对 函数本身 进行操作。
  • Foo.fun() 和 Foo().fun() 的区别。
  • new Foo.fun() 和 new Foo().fun() 的优先级问题。
  • new new Foo().fun() 的优先级。

2. 普通函数和箭头函数

function A() {
	alert(1);
}
function Func() {
	A = function() {
		alert(2);
	}
	return this;
}
Func.A = A;
Func.prototype = {
	A: ()=>{
		alert(3);
	}
}
A(); // => "1"
Func.A(); // => "1"
Func().A(); // => "2"
new Func.A(); // => "1"
new Func().A(); // => "3"
new new Func().A(); // => 报错

注意:

  • 函数在赋值时 不存在 地址赋值
  • 箭头函数中不会绑定 this,因此箭头函数不能被 new。

六、事件循环机制

`

async function async1() {
	console.log('async1 start');
	await async2();
	console.log('async1 end');
}

async function async2() {
	console.log('async2');
}

console.log('script start');
setTimeout(function(){
	console.log('setTimeout1');
}, 100);
setTimeout(function(){
	console.log('setTimeout2');
}, 0);


async1();
new Promise((res, rej)=>{
	console.log('promise1');
	res();
}).then(data=>{
	console.log('promise2');
});

console.log('script end');
/* 
=> script start -> async1 start -> async2 
->  promise1 -> script end -> async1 end -> 
promise2 -> setTimeout2 -> setTimeout1
*/

知识点:

  • 主任务,微任务、宏任务的执行顺序为:主 > 微 > 宏
  • async 创建的函数执行时,使用 await 之后会产生什么特点。
  • promise创建的任务为微任务,但是注意 构造函数 中传入的 执行函数却是主任务,因为 new 是一个主任务

七、趣味JavaScript

1. 你不知道的==运算符

[] == [] // => false

[] == ![] // => true

[] == ![][] // =>fase

分析:

  • 对象,数组,函数在==中是判断地址。
  • 当两边数据类型不一致,同时含有对象、数组时会进行转换: -> toString() -> Number()
  • 数组的toString为所有的集合
{} == {} // => false

{} == !{} // => false

{} == !!{} // => false

注意:

  • 六种情况转换为boolean为fales,其他都为true。分别是: “”, 0, undefined, null, NaN, false。
NaN == NaN // => false
undefined == null // => true

注意:

  • NaN和自己比较都是false,那么NaN和其他比较都是false
  • undefined == null为true。

2. a在什么情况下,下面等式成立?

a == 1 && a == 2 && a == 3

① a为对象

let a = {i:0};
a.toString = function() {
	return ++this.i;
}
a.valueOf = function() {
	return ++this.i;
}

if(a == 1 && a == 2 && a == 3){
	console.log('条件成立');
}

注意:

  • 对象和其他类型判断时,会触发自己的 valueOf()和String() 方法。
  • 原始valueOf()和toString() 之间的区别:
    1. valueOf函数返回的依然是数据的本身,而toString()返回的是一个字符串。
    2. valueOf先于toString之前执行。

② a为window的属性

var i = 0;
Object.defineProperty(window, 'a', {
	get: function() {
		return ++i;
	}
})

if(a == 1 && a == 2 && a == 3){
	console.log('条件成立');
}

思路:

  • 使用了Object.defineProperty() 进行了数据的劫持
  • 需要注意该api不允许对经定义的属性进行访问,因此借用了第三变量 i 做了一个中间的桥梁。

③ a为数组

let a = [1, 2, 3];
a.toString = shift;

if(a == 1 && a == 2 && a == 3){
	console.log('条件成立');
}

分析:

  • 原理和第一种相似,在这里不再重复说明。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值