以下总结只是个人看法,不足地方请见谅
一、浏览器的原理(v8内核)——js执行原理
一个页面在浏览器的运行过程,如图下:
①首先是html和css共同运行到Attachment 。
②然后再通过Render生成每一个标签树。
③通过layout进行代码的布局,其中这里可能会有js操作,那这里就会借助js引擎帮助js操作DOM节点。
④之后就生成了完整的页面。
二、变量作用域,函数作用域
开胃知识:首先会有一个全局对象 globalObject(简称:GO),它里面有很多你会用到东西比如:window,console.log,setTimeout,String,Date等等,如下图:
1.变量作用域:
注:每次进行变量命名的时候都会先去GO对象里面找看有没有这个变量,真是的查找路径是沿着作用域链来查找的
var name1;
console.log(name1);//undefined //一般不要使用name
2.函数作用域:
注:每次进行函数命名的时候都会先去创建AO对象=>再去GO对象里面找看有没有这个变量
// aaa();函数提升
function aaa() {
console.log(1111);
}
aaa();//正常执行
3.作用域链:
为什么在child函数里面还能打印出age1的值呢:原因是作用域链首先child函数调用AO没有找到age1的变量,就是继续向父级函数的AO里找age1的变量,最终找到GO,找到的话就赋值,没有的话就是提示未定义的报错。
var age1 = "18";
age(12);
function age(params) {
console.log(params);//12
function child() {
console.log(age1);//18
}
child();
}
三、闭包的定义,内存模型
1.闭包
开胃知识:闭包和高阶函数(一个函数可以接受另一个函数作为参数或者返回的值为一个函数)是密不可分的。 一个函数能访问到外层作用域函数的变量,那么他就是闭包。
几个常见的高阶函数,如下:
// 1.filter过滤
let number = [12, 51, 65, 22, 32];
// 原始版
// var ordr = number.filter(function (item) {
// return item % 2 === 0;
// });
// 简化版
var ordr = number.filter((item) => item % 2 === 0);
console.log(ordr); // [12, 22, 32]
// 2.map映射
// 原始版
// var ordr1 = number.map(function (item) {
// return item % 2 === 0;
// });
// 简化版
var ordr1 = number.map((item) => item * 10);
console.log(ordr1); // [120, 510, 650, 220, 320]
// 3.reduce:累加
var ordr2 = number.reduce(function (value, item) {
return value + item;
}, 0);
console.log(ordr2); //182
2.内存
定义:分成栈(主要是执行上下文的操作)和堆(执行全局GO对象,函数AO对象,用到作用域链一些相互之间的操作。)
内存泄漏:如果一个函数不用了就会出现内存泄漏的情况(一般闭包的话可能会出现内存泄漏的问题),解决如下图:。
四、this指向
盲区:不一定是谁定义了this,就指向的谁。 箭头函数:箭头函数的this是没有的 他会去他的上一级找this
①独立函数绑定——一般指向都是window。
②隐式函数绑定——谁调用了就会被js引擎绑定到该函数的this中。
③显示绑定——call,apply,bind 指定this绑定的对象。
④new
// 1.独立函数绑定
function fo() {
console.log(this);
}
let fn=fo();
fn()//绑定指向都是window
// 2.隐式绑定
var obj={
age:11,
eating:fn
}
obj.eating()//会被js引擎绑定到fn函数中的this中 {age:11,eating:function}
// 3.显示绑定——call,apply,bind 指定this绑定的对象
fo.call(obj)
fo.apply(obj)
fo.apply('abc')
//4.特殊绑定间接函数绑定
fo.call(null)//window fo.call(undefined)//window
1.call原型源码仿写
Function.prototype.hycall=function(thisArg,...arg){
//在这里可以去执行调用的那个函数
//问题:得可以获取到时哪个函数调用到call
var fn =this
// 2.对传过来的是进行判断转成对象,防止传过来的值是非对象
thisArg = (thisArg !==null && thisArg !==undefined) ? Object(thisArg) : window thisArg.fn=fn
thisArg.fn(...arg)
delete thisArg.fn
}
function foo() {
console.log('foo被执行了',this);
}
function sum(num1,num2) {
console.log('sum被执行了',this,num1,num2);
}
foo.hycall('abc')
sum.hycall('abc',12,22)
2.apply原型源码仿写
Function.prototype.hyapply=function(thisArg,argArray){
//在这里可以去执行调用的那个函数
//问题:得可以获取到时哪个函数调用到apply
var fn =this
// 2.对传过来的是进行判断转成对象,防止传过来的值是非对象
thisArg = (thisArg !==null && thisArg !==undefined) ? Object(thisArg) : window thisArg.fn=fn
var result =argArray || [] //逻辑或
thisArg.fn(...argArray)
delete thisArg.fn
return result
}
function foo() {
console.log('foo被执行了',this);
}
function sum(num1,num2) {
console.log('sum被执行了',this,num1,num2);
}
sum.hyapply('abc',[12,22])
3.bind原型源码仿写
Function.prototype.hybind=function(thisArg,...argArray){
// 1.获取到真是需要调用的函数
var fn =this
// 2.绑定this
thisArg = (thisArg !==null && thisArg !==undefined) ? Object(thisArg) : window
function proxyFn(...arg) {
thisArg.fn =fn
// 3.将函数放到总数组中
var sumArray=[...argArray,...arg]
var result =thisArg.fn(...sumArray)
delete thisArg.fn
return result
}
return proxyFn
}
function foo() {
console.log('foo被执行了',this);
}
function sum(...num) {
console.log('sum被执行了',this,...num);
}
var newSum= sum.hybind('abc',12,22)
var result =newSum(55,33)