原型链(别名:隐式原型链)
作用: 查找对象的属性(方法)
访问一个对象的属性时:
- 先在自身属性中查找,找到返回
- 如果没有,再沿着__proto__这条链向上查找,找到就返回
- 如果最终没有找到,放回undefined
- 函数的显示原型指向的对象是空Object实例对象(但Object不满足)
- 所有的函数都是Function的实例(包含Function)
- Object 原型对象是原型链的尽头
读取对象的属性值时:会自动到原型链中查找
设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
function AA() {
}
AA.prototype.a = 'xxx';
var fn1 = new AA();
console.log(fn1.a, fn1);
var fn2 = new AA();
fn2.a = 'yyy';
console.log(fn2.a, fn2);
// 函数的显示原型指向的对象是空Object实例对象(但Object不满足)
console.log(myfn.prototype instanceof Object);
console.log(Function.prototype instanceof Object);
console.log(Object.prototype instanceof Object);
// 所有的函数都是Function的实例(包含Function)
console.log(Function.__proto__ === Function.prototype);
// Object 原型对象是原型链的尽头
console.log(Object.prototype.__proto__);//值为null
变量提升与函数提升
var a = 3;
function test() {
console.log(a);
var a = 4;
}
test();
输出是undefined 函数体内相当于执行了
var a; console.log(a); a=4;
var a = 3;
function test() {
console.log(a);
// var a = 4;
}
test();
//此时输出3
变量(声明)提升 :通过var定义的变量,在定义语句之前就可以访问到,只不过此时值为undefined
函数(声明)提升: 通过function声明的函数,在之前就可以直接调用,值是函数定义(对象)
console.log(b); //变量声明提升
fn2(); //可调用 函数(声明)提升
var b = 3;
function fn2() {
console.log("fn2()");
}
全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理: var定义的全局变量===》undefined,添加为window的属性 function声明的全局函数,添加为window的方法 this赋值为window 然后开始执行全局代码。
函数执行上下文
- 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟)
- 对局部数据进行预处理: 形参–> 实参(赋值)–>添加为执行上下文的属性
arguments --》实参列表(赋值)–> 添加为执行上下文的属性
var定义的局部变量–> undefined ,添加为执行上下文的属性
function声明的函数–> 赋值(fun),添加为执行上下文的方法
this–》赋值(调用函数的对象)
开始执行函数体代码
console.log("函数执行上下文");
function myfn(a1) {
console.log(a1); //2
console.log(a2); //undefined
a3(); //a3()
console.log(this); //window
console.log(arguments); // 伪数组(2,3)
var a2 = 3;
function a3() {
console.log("a3()");
}
}
myfn(2, 3);
函数闭包
产生闭包的条件:
函数嵌套
内部函数引用了外部函数的数据(变量/函数)
闭包的生命周期:
产生 在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡 在嵌套内部函数成为垃圾对象时
闭包的缺点
- 容易造成内存泄露
- 函数执行完后,函数内的局部变量没有被释放,占用内存时间会边长
如何解决
及时释放 让某个对象=null,召唤垃圾回收器
例如 var f = fn1(); f(); f=null ; 让内部函数成为垃圾对象 —> 回收闭包
哪些情况可能会造成内存泄露
意外的全局变量
没有及时清理的定时器或者回调函数
闭包
闭包的应用 自定义js模块
推荐使用闭包 调用方便
function myModule1() {
var str = "zhOUdage";
function dosomething() {
console.log("dosomething()" + str.toUpperCase());
}
function doother() {
console.log("doother()" + str.toLowerCase());
}
return {
dosomething: dosomething,
doother: doother
};
}
// 使用闭包
(function() {
var str = "zhOUdage";
function dosomething() {
console.log("dosomething()" + str.toUpperCase());
}
function doother() {
console.log("doother()" + str.toLowerCase());
}
window.myModule2 = {
dosomething: dosomething,
doother: doother
};
})()
<script src="myModule.js"></script>
<script type="text/javascript">
var module = myModule1();
module.dosomething();
module.doother();
// 上面是方式一
myModule2.doother();
myModule2.dosomething();
</script>
继承
子类型的原型为父类型的一个实例对象
定时器真的是定时执行的吗
定时器并不能保证真正定时执行
一般会延迟一点儿(可以接受),但做一个长时间的工作,会延迟不能接受的时间
<head>
window.onload = function() {
var btn = document.getElementsByClassName("btn")[0];
btn.onclick = function() {
var date = Date.now();
console.log("定时器执行前");
setTimeout(function() {
console.log("延时", Date.now() - date);
}, 200);
console.log("定时器执行后");
for (var i = 0; i < 1000000000; ++i) {}
}
}
</script>
</head>
<body>
<button class="btn">点我延迟200</button>
</body>
js单线程执行,如何证明
代码如下
setTimeout(function() {
console.log("timeout 2222");
}, 2000)
setTimeout(function() {
console.log("timeout 111");
}, 1000);
setTimeout(function() {
console.log("timeout 000");
}, 0);
function fn() {
console.log("fn()");
}
fn();
console.log("alert之前");
alert("----------------");
// 暂停主线程执行 同时暂停计时
console.log("alert之后");
代码分类:
初始化代码 回调代码
js引擎执行代码的基本流程
- 先执行初始化代码:例如:绑定事件监听、设置定时器、发送ajax请求
- 后面在某个时刻才执行回调代码
为什么js使用单线程而不是多线程:
与它的用途有关,作为脚本语言,主要是与用户互动,以及操作dom,决定了它只能是单线程,否则会带来很复杂的同步问题
js分线程,不更新界面,适用于后台数据处理部分(算菲薄拉契数列例子)
主代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript">
window.onload = function() {
var btn = document.getElementById("btn");
var num = document.getElementById("number");
// 1 1 2 3
btn.onclick = function() {
// // alert("hello");
// alert(cal(parseInt(num.value)));
// 创建一个worker,并向他传递将在新线程中执行的脚本名
var worker = new Worker("worker.js");
// 向分线程发送消息
worker.postMessage(num.value);
// 接受worker传过来的数据函数
worker.onmessage = function(event) {
console.log("主线程收到的数据" + event.data);
}
}
}
</script>
</head>
<body>
<input type="text" id="number" placeholder="要计算的值">
<button id="btn">计算菲薄拉起数列</button>
</body>
</html>
分线程代码
function cal(n) {
return n <= 2 ? 1 : cal(n - 1) + cal(n - 2);
}
var onmessage = function(event) {
console.log("分线程收到的数据", event.data);
var tmp = cal(parseInt(event.data));
// 将处理后的数据发送给主线程
postMessage(tmp);
}