赞
踩
1.分类:
-基本类型
-String:任意字符串
-Number:任意的数字
-boolean: true/false
-undefined:未定义
-null:空
-引用(对象)类型
-Object:任意对象
-Function:一种特别的对象(可以执行)
-Array:一种特别的对象(数值下标,内部数据是有序的)
2.判断:
-typeof:
可以判断:undefined、数值、字符串、布尔值
不能判断: null与object object与array
instanceof:可以判断对象的具体类型
=== 可以判断undefined、null
undefined与null的区别?
* undefined表示定义未赋值
* null表示赋值了,值为null
什么时候给变量赋值为null?
* 初始赋值,表明将要赋值为对象
* 结束前,让对象成为垃圾对象(被垃圾回收器回收)
3.严格区别变量类型与数据类型?
* 数据的类型
- 基本类型
- 对象类型
* 变量的类型(变量内存值的类型)
-基本类型:保存就是基本类型的数据
-引用类型:保存的是地址值
1.什么是数据?
-存储在内存中代表特定的信息,本质上是010101…
-数据的特点:可传递、可运算
-内存中所有操作的目标:数据
逻辑运算
算数运算
赋值
运行函数
2.什么是内存?
-内存条通电后产生的可储存数据的空间(临时的)
-内存产生和死亡:内存条==>通电==>产生内存空间==>存储数据==>处理对象==>断电==>内存空间和数据消失
-一小块内存的2个数据
-内部存储的数据
-地址值
-内存分类
-栈:存储全局变量、局部变量
-堆:存储对象
3.什么是变量?
-可变化的的量,又变量名和变量值组成
-每个变量都对应的一小块内存,变量名用来查找对应的内存,变量值就是内存中保存的数据
4.内存,数据,变量三者之间的关系
-内存用来存储数据
-变量是内存的标识
5.var a = xxx; a内存中到底保存的是什么?
- xxx 是基本数据,保存的就是这个数据
- xxx 是对象,保存的是对象的地址值
- xxx 是一个变量,保存的xxx的内存内容(可能是基本数据,也可能是地址值)
6.关于引用变量的赋值问题:
2个引用变量指向同一个对象,通过一个变量修改对象内部数据,另一个变量看到的是修改之后的数据。
2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一个引用变量依然指向前一个对象。
<script> var obj1 = {name:"tom"}; var obj2 = obj1; obj2.name = "bob"; console.log(obj1.name); //bob function fun(obj){ obj.name = "TIMI"; }; fun(obj1); console.log(obj2.name); //TIMI var a = {age:12}; var b = a; a = {name:"mike",age:13}; b.age = 14; console.log(b.age,a.name,a.age); //14,mike,13 function fun2(obj){ obj = {age:15}; }; fun2(a); console.log(a.age); //13 </script>
7.在js调用函数时传递变量参数时,是值传递还是引用传递?
- 理解1:都是值(基本/地址值)传递
- 理解2:可能是值传递,也可能是引用传递(地址值)、
<script>
var a = 3;
function fun1(a){
a = a+1;
};
fun1(a);
console.log(a);//3
function fun2(obj){
console.log(obj.name);
};
var obj = {name:"tom"};
fun2(obj);
</script>
8.js引擎如何管理内存
1.内存生命周期
- 分配小内存空间,得到它的使用权
- 储存数据,可以反复进行操作
- 释放小内存空间
2.释放内存
-局部变量:函数执行完自动释放
对象:成为垃圾对象–>垃圾回收器回收
1.什么是对象?
- 多个数据的封装体
- 用来保存多个数据的容器
- 一个对象代表现实中的一个事物
2.为什么用对象?
- 统一管理多个数据
3.对象的组成
- 属性:属性名(字符串)和属性值(任意)组成
- 方法:一种特别的属性(属性值是函数)
4.如何访问对象内部的数据?
- .属性名: 编码简单,有时不能用
- [“属性名”]: 编码麻烦,能通用
5.什么时候使用 [“属性名”] 这种方式
- 属性名包含特殊字符:- 空格等
- 属性名不确定
<script>
var p = {};
// 1.给对象p添加一个属性:content-type:text/json
// p.content-type = "text/json"; 不能用
p["content-type"] = "text/json";
console.log(p["content-type"]);
// 2.属性名不确定
var propName = "myAge";
var value = 18;
// p.propName = value; 不能用
p[propName] = value;
console.log(p[propName]);
</script>
什么是函数?
实现特定功能的n条语句的封装体
只有函数是可以执行的,其他类型的数据不能执行
如何定义函数?
函数声明
表达式
如何调用函数?
test(); 函数名(); 直接调用
obj.test(); 通过对象调用
new test(); new调用
test.call/apply(obj); 临时让test成为obj的方法进行调用
什么是回调函数?
1.自己定义的
2.没有调用
3.最终执行了(在某个时刻或某个条件下)
常见的回调函数?
1.dom事件回调函数
2.定时器的回调函数
3.ajax请求回调函数
4.生命周期回调函数
<body> <button id="btn01">回调</button> <script> var btn01 = document.getElementById("btn01"); btn01.onclick = function(){ alert(this.innerHTML); }; // 定时器 setTimeout(function(){ alert("到点了"); },2000); </script> </body>
IIFE 全称:Immediately-Invoked Function Expression (立即调用函数表达式),别名:匿名函数自调用。
作用:
- 隐藏内部实现
-不会干扰到外部(全局)命名空间
-用它来编码js模块
<script> (function(){ //匿名函数自调用 var a = 3; console.log(a+3); })(); var a = 4; console.log(a); (function(){ var a = 1; function test(){ console.log(++a); } window.$ = function(){ //向外暴露一个全局函数 return{ test:test } }; })(); $().test(); //$是一个函数 //$执行后返回的是一个对象 </script>
this是什么?
- 在任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
- 所有函数内部都有一个变量this
- 它的值是调用函数的当前对象
如何确定this的值?
- test(); window
- p.test(); p
- new test(); 新创建的对象
- p.call(obj); obj
<script type="text/javascript"> function Person(color) { 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("yello"); //this是谁? p p.getColor(); //this是谁? p var 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>
1.函数的prototype属性
- 每个函数都有一个prototype属性,它默认指向一个Object空对象(即为原型对象)
- 原型对象中有一个属性constructor,它指向函数对象
2.给原型对象添加属性(一般都是方法)
- 作用:函数的所有实例对象自动拥有原型中的属性(方法)
<script> // 每个函数都有一个prototype属性,它默认指向一个Object空对象(即为原型对象) console.log(Date.prototype,typeof Date.prototype); function Fun(){ } console.log(Fun.prototype);//默认指向一个Object空对象(没有我们的属性) // 原型对象中有一个属性constructor,它指向函数对象 console.log(Date.prototype.constructor===Date); console.log(Fun.prototype.constructor===Fun); // 给原型对象添加属性(一般是方法)--> 实例对象可以访问 Fun.prototype.test = function(){ console.log('test()'); } var fun = new Fun(); fun.test(); </script>
1.每个函数function都有一个prototype,即显示原型(属性)
2.每个实例对象都有一个__proto__,可称为隐式原型(属性)
3.对象的隐式原型的值为其对应构造函数的显示原型的值
4.总结:
- 函数的prototype属性:在定义函数时自动添加的,默认值是一个空Object对象
- 对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
- 能直接操作显示原型,但不能直接操作隐式原型(ES6之前)
<script> // 定义构造函数 function Fn(){ // 内部语句:this.prototype = {} } // 1.每个函数function都有一个prototype,即为显示原型属性,默认指向一个空的Object对象 console.log(Fn.prototype); // 2.每个实例对象都有一个__protp__,可称为隐式原型 // 创建实例对象 var fn = new Fn(); // 内部语句:this.__proto__ = Fn.prototype console.log(fn.__proto__); // 3.对象的隐式原型的值为其对应构造函数的显示原型的值 console.log(Fn.prototype === fn.__proto__); //true // 给原型对象添加方法 Fn.prototype.test = function(){ console.log("test()"); } // 通过实例调用原型的方法 fn.test(); </script>
显示原型与隐式原型的内存结构图
原型链(别名:隐式原型链)
访问一个对象属性时,
先在自身属性中查找,找到返回
如果没有,再沿着__proto__这条链向上查找,找到返回
如果最终没有找到,则返回undefined
作用:
1.查找对象的属性(方法)
2.构造函数/原型/实体对象的关系(图解)
3.构造函数/原型/实体对象的关系2(图解)
<script> // console.log(Object); // console.log(Object.prototype); console.log(Object.prototype.__proto__); 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(); fn.test2(); console.log(fn.toString()); console.log(fn.test3); // fn.test3(); /* 1.函数的显示原型指向的对象默认是空Object实例对象(但Object不满足) */ 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); /* Object的原型对象是原型链的尽头 */ console.log(Object.prototype.__proto__); //null </script>
原型链图解
1.读取对象的属性值时,会自动到原型链中查找
2.设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
<script> function Fn(){ } Fn.prototype.a = "xxx"; var fn1 = new Fn(); console.log(fn1.a,fn1); var fn2 = new Fn(); fn2.a = "yyy"; console.log(fn1.a,fn2.a,fn2); function Person(name,age){ this.name = name; this.age = age; } Person.prototype.setName = function(name){ this.name = name; }; var p1 = new Person("Tom",12); p1.setName("libai"); var p2 = new Person("jack",12); p2.setName("nice"); console.log(p1); console.log(p2); console.log(p1.__proto__ === p2.__proto__); //true </script>
1.instanceof是如何判断的?
表达式:A instanceof B
如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false
2.Function是通过new自己产生的实例
<script> /* 案例1 */ function Foo(){} var f1 = new Foo(); console.log(f1 instanceof Foo); //true console.log(f1 instanceof Object); //true /* 案例2 */ console.log(Object instanceof Function); //true console.log(Object instanceof Object); //true console.log(Function instanceof Function); //true console.log(Function instanceof Object); //true function Foo(){}; console.log(Object instanceof Foo); //false </script>
<script> /* 测试题1*/ function A(){} A.prototype.n = 1; var b = new A(); A.prototype = { n:2, m:3 }; var c = new A(); console.log(b.n,b.m,c.n,c.m); //1 undefined 2 3 /* 测试题2 */ function F(){} Object.prototype.a = function(){ console.log("a()"); }; Function.prototype.b = function(){ console.log("b()"); }; var f = new F(); f.a(); // f.b(); 不能执行 F.a(); F.b(); console.log(f); console.log(Object.prototype); console.log(Function.prototype); </script>
测试一图解
1.变量的声明提升
2.函数声明提升
3.问题:变量提升和函数提升是如何产生的?
<script> /* 面试题 */ // 输出a的值 var a = 3; function fn(){ console.log(a); var a = 4; } fn(); //a=undefined console.log(b); //undefined 变量提升 fn2(); //可调用 函数提升 // fn3(); 不能调用,变量提升 var b = 3; function fn2(){ console.log("fn2()"); } var fn3 = function(){ console.log("fn3()"); }; </script>
1.代码分类(位置)
- 全局代码
- 函数(局部)代码
2.全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理
* var 定义的全局变量–>undefined,添加为window的属性
* function声明的全局函数–>赋值(fun),添加为window的方法
* this–>赋值(window)
- 开始执行全局代码
3.函数执行上下文
- 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
- 对局部数据进行预处理
* 形参变量–>赋值(实参)–>添加为执行上下文的属性
* arguments–>赋值(实参列表),添加为执行上下文属性
* var 定义的局部变量–>undefined,添加为执行上下文的属性
* function声明的函数–>赋值(fun),添加为执行上下文的方法
* this–>赋值(调用函数的对象)
- 开始执行函数体代码
<script> /* 全局执行上下文 */ console.log(a1,window.a1); //undefined,undefined a2(); //a2() console.log(this); //window var a1 = 3; function a2(){ console.log("a2()"); } console.log(a1); //3 console.log("========="); /* 函数执行上下文 */ function fn(a1){ console.log(a1); console.log(a2); a3(); console.log(this); console.log(arguments); //伪数组(2,3) var a2 = 3; function a3(){ console.log("a3()"); } } fn(2,3); //2,undefined,a3(),window </script>
1.在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3.在函数执行上下文创建后,将其添加到栈中(压栈)
4.在当前函数执行完后,将栈顶的对象移除(出栈)
5.当所有的代码执行完之后,栈中只剩下window
<script>
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);
};
bar(10); //30
</script>
<script> /* 1.以下代码依次输出什么? gb:undefined fb:1 fb:2 fb:3 fe:3 fe:2 fe:1 ge:1 2.整个过程中产生了几次执行上下文? 5次 */ console.log("gb:"+i); var i = 1; foo(1); function foo(i){ if(i==4){ return; } console.log("fb:"+i); foo(i + 1); //递归调用 console.log("fe:"+i); } console.log("ge:"+i); </script>
<script> /* 测试题1 先执行变量提升,再执行函数提升 */ function a(){} var a; console.log(typeof a); //function /* 测试题2 */ if(!(b in window)){ var b = 1; } console.log(b); //undefined /* 测试题3 */ var c = 1; function c(c){ console.log(c); var c = 3; } c(2); //报错,c不是函数 </script>
作用域
1.理解:
- 指一块空间,代码所在的区域
- 它是静态的(相对于上下文对象),在编写代码时就确定了
2.分类:
- 全局作用域
- 函数作用域
- 没有块作用域(ES6开始有了)
3.作用:
- 隔离变量,不同作用域下同名变量不会有冲突
<script> // 没有块作用域 /* if(true){ var c = 3; } console.log(c); */ var a = 10; var b = 20; function fn(x){ var a = 100; c = 300; console.log("fn()",a,b,c,x); function bar(x){ var a = 1000; var d = 400; console.log("bar()",a,b,c,d,x); } bar(100); bar(200); } fn(10); </script>
作用域与执行上下文的区别:
区别1
- 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时,
- 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
- 函数执行上下文是在调用函数时,函数体代码执行之前创建
区别2
- 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
- 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
联系
- 上下文环境(对象)是从属于所在的作用域
- 全局上下文环境–>全局作用域
- 函数上下文环境–>对应的函数作用域
<script> var a = 10; var b = 20; function fn(x){ var a = 100; c = 300; console.log("fn()",a,b,c,x); function bar(x){ var a = 1000; var d = 400; console.log("bar()",a,b,c,d,x); } bar(100); bar(200); } fn(10); </script>
1.理解
- 多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)
- 查找变量时就是沿着作用域链来查找的
2.查找一个变量的查找规则
- 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
- 在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
- 再次执行2的相同操作,知道全局作用域,如果还找不到就抛出找不到的异常
<script> /* 作用域链 */ var a = 1; function fn1(){ var b = 2; function fn2(){ var c = 3; console.log(c); console.log(b); console.log(a); console.log(d); //d未定义,会报错 } fn2(); } fn1(); </script>
<script> var x = 10; function fn(){ console.log(x); } function show(f){ var x = 20; f(); } show(fn); //10 // fn的作用域没有x,只能从外部的作用域去找 console.log("========="); var fn = function(){ console.log(fn); }; fn(); var obj = { fn2:function(){ console.log(fn2); //会报错,fn2未被定义 // console.log(this.fn2); 想要输出该作用域的fn2,须在前面加this. } }; obj.fn2(); </script>
1.如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
2.闭包到底是什么?
理解一:闭包是嵌套的内部函数
理解二:包含被引用变量(函数)的对象
注:闭包存在于嵌套的内部函数中
3.产生闭包的条件?
函数嵌套
内部函数引用了外部函数的数据(变量/函数)
<script>
function fn1(){
var a = 2;
var b = "abc";
function fn2(){ //执行函数定义就会产生闭包(不用调用内部函数)
console.log(a);
}
fn2();
}
fn1();
</script>
1.将函数作为另一个函数的返回值
2.将函数作为实参传递给另一个函数调用
<script> // 1.将函数作为另一个函数的返回值 function fn1(){ var a = 2; function fn2(){ a++; console.log(a); } return fn2; } var f = fn1(); f(); //3 f(); //4 // 2.将函数作为实参传递给另一个函数调用 function showDelay(msg,time){ setTimeout(function(){ alert(msg); },time); } showDelay("学 习",2000); </script>
闭包的作用
1.使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
1.函数执行完后,函数内部声明的局部变量是否还存在?
一般是不存在的,存在于闭包的变量才可能存在
2.在函数外部能直接访问函数内部的局部变量么?
不能,但我们可以通过闭包让外部操作它
<script> function fn1(){ var a = 2; function fn2(){ a++; console.log(a); } function fn3(){ a--; console.log(a); } return fn3; } var f = fn1(); f(); //1 f(); //0 </script>
1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
2.死亡:在嵌套的内部函数成为垃圾对象时
<script> function fn1(){ // 此时闭包就已经产生了(函数提升,内部函数对象已经创建了) var a = 2; function fn2(){ a++; console.log(a); } return fn2; } var f = fn1(); f(); //3 f(); //4 f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象) </script>
闭包的应用:自定义JS模块
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包,执行n个方法的对象或函数
- 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
<script src="myModule2.js"></script>
<script>
myModule2.doSomething();
myModule2.doOtherthing();
</script>
myModule2.js
(function(){ // 私有数据 var msg = "My Class"; // 操作数据的函数 function doSomething(){ console.log("doSomething()"+msg.toUpperCase()); } function doOtherthing(){ console.log("doOtherthing()"+msg.toLowerCase()); } // 向外暴露对象(给外部使用的方法) window.myModule2 = { doSomething:doSomething, doOtherthing:doOtherthing } })()
1.缺点:
- 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
- 容易造成内存泄漏
2.解决
- 能不用闭包就不用
- 及时释放
<script>
function fn1(){
var arr = new Array[100000];
function fn2(){
console.log(arr.length);
}
return fn2;
}
var f = fn1();
f();
f = null; //让内部函数成为垃圾对象-->回收闭包
</script>
1.内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超过了剩余内存时,就会抛出内存溢出的错误
2.内存泄漏
- 占用的内存没有及视释放
- 内存泄漏积累的多了就容易导致内存溢出
常见的内存泄漏:
* 意外的全局变量
* 没有及时清理的计时器或回调函数
* 闭包
<script> // 内存溢出 var obj = {}; for(var i = 0;i<10000;i++){ obj[i] = new Array(1000000); console.log("-----"); } // 内存泄漏 // 意外的全局变量 function fn(){ a = new Array(1000000); console.log(a); } fn(); // 没有及时清理的计时器或回调函数 var start = setInterval(function(){ //启动定时器后不清理 console.log("---"); },2000); // 清理定时器 clearInterval(start); // 闭包 function fn1(){ var a = 4; function fn2(){ console.log(++a); } return fn2; } var f = fn1(); f(); // f = null; </script>
<script> /* 以下代码输出的是什么 */ // 代码片段一 var name = "The Window"; var object = { name:"My Object", getNameFunc:function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); // the window // 函数嵌套,但是内部函数没有调用外部函数的变量,不是闭包 // 代码片段二 var name2 = "The Window"; var object2 = { name2:"My Object", getNameFunc:function(){ var that = this; return function(){ return that.name2; }; } }; alert(object2.getNameFunc()()); //My Object // 函数嵌套,内部函数调用了外部函数的that,是闭包 </script>
<script> function fun(n,o){ console.log(o); return{ fun:function(m){ return fun(m,n); } } } var a = fun(0); a.fun(1); a.fun(2); a.fun(3); // undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3); //undefined,0,1,2 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined,0,1,1 </script>
方式一:Object构造函数模式
- 套路:先创建空Object对象,再动态添加属性/方法
- 使用场景:起始时不确定对象内部数据
- 问题:代码量多
<script>
var p = new Object();
p.name = "tom";
p.age = 12;
p.setName = function(name){
this.name = name;
};
// 测试
p.setName("李白");
console.log(p.name,p.age);
</script>
方式二:对象字面量模式
- 套路:使用{}创建对象,同时指定属性和方法
- 适用场景:起始时对象内部数据时确定的
- 问题:如果创建多个对象,有重复代码
<script>
var p = {
name:"tom",
age:16,
setName:function(name){
this.name = name;
}
};
// 测试
console.log(p.name,p.age);
p.setName("Jack");
console.log(p.name,p.age);
</script>
方式三:
工厂模式
- 套路:通过工厂函数动态创建对象并返回
- 适用场景:需要创建多个对象
- 问题:对象没有一个具体的类型,都是Object类型
<script> function createPerson(age,name){ //返回一个对象的函数-->工厂函数 var obj = { age:age, name:name, setName:function(){ this.name = name; } }; return obj; } var p1 = createPerson(16,"张三"); var p2 = createPerson(16,"李三"); console.log(p1); console.log(p2); function createCat(age,name){ var obj = { age:age, name:name, setName:function(){ this.name = name; } }; return obj; } var c1 = createCat(15,"波斯猫") var c2 = createCat(15,"野猫") console.log(c1); console.log(c2); </script>
方式四:自定义构造函数模式
套路:自定义构造函数,通过new创建对象
适用场景:需要创建多个类型确定的对象
问题:每个对象都有相同的数据,浪费内存
<script> // 定义类型 function Person(name,age){ this.name = name; this.age = age; this.setName = function(){ this.name = name; }; } var p1 = new Person("lisa",16); p1.setName("bob"); console.log(p1.name,p1.age); console.log(p1 instanceof Person); function Student(name,price){ this.name = name; this.price = price; this.setName = function(){ this.name = name; }; } var s = new Student("熊大",1000); console.log(s instanceof Student); var p2 = new Person("Jack",16); console.log(p1,p2) </script>
方式五:构造函数+原型组合
套路:自定义构造函数,属性在函数中初始化,方法添加到原型上
适用场景:需要创建多个类型确定的对象
<script>
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.setName = function(name){
this.name = name;
};
var p1 = new Person("tom",16);
var p2 = new Person("bob",19);
console.log(p1,p2);
</script>
原型链继承:方式一
套路:
1.定义父类型的构造函数
2.给父类型的原型添加方法
3.定义子类型的构造函数
4.创建父类型的对象赋值给子类型的原型
5.将子类型原型的构造属性设置为子类型
6.给子类型原型添加方法
7.创建子类型的对象:可以调用父类型的方法
关键
- 子类型的原型为父类型的一个实例对象
<script> // 父类型 function Supper(){ this.supProp = "Supper property"; } Supper.prototype.showSupperProp = function(){ console.log(this.supProp); } // 子类型 function Sub(){ this.subProp = "Sub property"; } // 子类型的原型为父类型的一个实例对象 Sub.prototype = new Supper(); // 让子类型的原型的constructor指向子类型 Sub.prototype.constructor = Sub; Sub.prototype.showSubProp = function(){ console.log(this.subProp); } var sub = new Sub(); sub.showSupperProp(); sub.showSubProp(); console.log(sub); //Sub </script>
原型链继承结构图:
方式二:借用构造函数继承(假的)
1.套路:
- 定义父类型的构造函数
- 定义子类型的构造函数
- 在子类型构造函数中调用父类型构造
2.关键:
- 在子类型构造函数中通用call()调用父类型构造函数
<script> function Person(name,age){ this.name = name; this.age = age; } function Student(name,age,price){ Person.call(this,name,age); //相当于:this.Person(name,age) /* this.name = name; this.age = age; */ this.price = price; } var s = new Student("tom",20,14000); console.log(s.name,s.age,s.price); </script>
方式三:原型链+借用构造函数的组合继承
1.利用原型链实现对父类型对象的方法继承
2.利用call借用父类型构造函数初始化相同属性
<script> function Person(name,age){ this.name = name; this.age = age; } Person.prototype.setName = function(name){ this.name = name; }; function Student(name,age,price){ Person.call(this,name,age); //为了得到属性 this.price = price; } Student.prototype = new Person(); //为了能看到父类型的方法 Student.prototype.constructor = Student; //修正constructor属性 Student.prototype.setPrice = function(price ){ this.price = price; }; var s = new Student("tom",24,15000); s.setName("bob"); s.setPrice(16000); console.log(s.name,s.age,s.price); </script>
进程:程序的一次执行,它占有一片独有的内存空间,可以通过windows任务管理器查看进程
- 多进程运行:一应用程序可以同时启动多个实例运行
线程:是进程内的一个独立执行单元,试程序执行的一个完整流程,是CPU的最小调度单元
多线程:在一个进程内,同时有多个线程运行
优点:
1.有效提升CPU的利用率
缺点:
1.创建多线程开销
2.线程间切换开销
3.死锁与状态同步问题
单线程:
优点:顺序编程简单易懂
缺点:效率低
相关知识:
- 应用程序必须运行在某个进程的某个路线上
- 一个进程中至少有一个运行的线程:主线程,进程启动后自动创建
- 一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
- 一个进程内的数据可以供其中的多个线程直接共享
- 多个进程之间的数据是不能直接共享的
- 线程池:保存多个线程对象的容器,实现线程对象的反复利用
1.定时器真的是定时执行的么?
- 定时器并不能保证真正定时执行
- 一般会延迟一点,也有可能延迟很长时间
2.定时器回调函数是在分线程执行的吗?
- 在主线程执行的,js是单线程的
3.定时器是如何实现的?
事件循环模型
<button id="btn">启动定时器</button> <script> document.getElementById("btn").onclick = function(){ var start = Date.now(); console.log("启动定时器前"); setTimeout(function(){ console.log("定时器执行了",Date.now()-start); },200); } console.log("定时器启动后"); // 做一个长时间的工作 for(var i = 0;i<1000000000;i++){ } </script>
1.如何证明js执行是单线程的?
- setTimeout()的回调函数是在主线程执行的
- 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
2.为什么js要用单线程模式,而不是用多线程模式?
- JavaScript的单线程,与它的用途有关
- 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM
- 这决定了它只能是单线程,否则会带来很复杂的同步问题
3.代码的分类:
- 初始化代码
- 回调代码
4.js引擎执行代码的基本流程
- 先执行初始化代码:包含一些特别的代码 回调函数(异步执行)
- 设置定时器
- 绑定事件监听
- 发送ajax请求
- 后面在某个时刻才会执行回调代码
1.所有代码分类
- 初始化执行代码(同步代码):包含绑定dom事件监听,设置定时器,发送ajax请求的代码
- 回调执行代码(异步代码):处理回调逻辑
2.js引擎执行代码的基本流程:初始化代码==>回调代码
3.模型的2个重要组成部分:
- 事件(定时器/DOM事件/Ajax)管理模块
- 回调队列
4.模型的运转流程
- 执行初始化代码,将时间回调函数交给对应模块管理
- 当事件发生时,管理模块会将回调函数及其数据添加到回调队列当中
- 只有当初始化代码执行完后(可能要一定时间),才会遍历读取回调队列中的回调函数执行
<button id="btn">测试</button> <script> function fn1(){ console.log("fn1()"); } fn1(); document.getElementById("btn").onclick = function(){ console.log("点击了btn"); }; setTimeout(function(){ console.log("定时器执行了"); },2000); function fn2(){ console.log("fn2()"); } fn2(); </script>
1.h5规范提供了js分线程的实现,取名为:Web Workers
2.相关API
- Worker:构造函数,加载分线程执行的js文件
- Worker.prototype.onmessage: 用于接收另一个线程的回调函数
- Worker.prototype.postMessage: 向另一个线程发送消息
3.不足:
- worker内代码不能操作DOM
- 不能跨域加载js
- 不是每个浏览器都支持这个新特性
<input type="text" placeholder="数值" id="number"> <button id="btn">计算</button> <script> var input = document.getElementById("number"); document.getElementById("btn").onclick = function(){ var number = input.value; // 创建一个Worker对象 var worker = new Worker("worker.js"); // 绑定接收消息的监听 worker.onmessage = function(event){ console.log("主线程接收分线程返回"); alert(event.data); }; // 向分线程发送信息 worker.postMessage(number); console.log("主线程向分线程发送数据"+number); }; </script>
worker.js
function fibonacci(n){ return n<=2? 1:fibonacci(n-1) + fibonacci(n-2); //递归调用 } console.log(this); var onmessage = function(event){ var number = event.data; console.log("分线程接收到主线程发送的数据:"+number); // 计算 var result = fibonacci(number); postMessage(result); console.log("分线程向主线程返回数据:"+result); // alert(result) alert是window的方法,在分线程不能调用 // 分线程中的全局对象不再是window,所以在分线程中不可能跟新界面 };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。