重学JavaScript
P1
五大主流浏览器
- IE浏览器(Internet explorer):Trident内核,也是俗称的IE内核;
- 谷歌浏览器(Chrome):Chrome内核,以前是Webkit内核,现在是Blink内核;
- 火狐浏览器(Firefox):Gecko内核,俗称Firefox内核;
- 苹果浏览器(Safari):Webkit内核;
- 欧朋浏览器(Opera):最初是自己的Presto内核,后来加入谷歌大军,从Webkit又到了Blink内核;
浏览器的历史
来自哔哩哔哩:
浏览器全球发展史(上)
浏览器全球发展史(下)
ECMA
欧洲计算机制造商协会(European Computer Manufacturers Association)。它是评估、开发、认可电信、计算机的标准的一个协会。它有一个标准清单,里面有很多的标准。其中ECMA-262 是脚本语言的规范:ECMAScript。
所以我们所说的ES5、ES6是一个标准。
编程语言
分为两个比较大的类:
- 编译型:
- 翻译过程:源码 -> 编译器 -> 机器语言 -> 可执行的文件
- 优点:不需要根据不同的系统平台进行移植(有相应的解释器就能执行)
- 解释型:
- 翻译过程:源码 -> 解释器 -> 解释一行就执行一行
JavaScript
组成:
- ECMAscript:
- 语法、变量、关键字、保留字、值、原始类型、引用类型运算、对象、继承、函数……
- 文档对象模型:DOM:Document Object Model
- W3C规范 获取修改添加删除HTML元素
- 浏览器对象模型:BOM:Browser Object Model
- 没有规范 滚动条 窗口宽高 冒泡捕获 键盘事件……
JS引擎是单线程的,但是可以模拟多线程
轮转时间片:短时间之内轮流执行两个或者多个任务的片段
步骤:
1.任务1 任务2
2.切分 任务1 任务2
3.随机排列这些任务片段
4.按照这个队列顺序将任务片段送进JS线程
4.JS线程执行一个又一个的任务片段
变量
变量就相当于是个容器,用来存储数据,作用是后续使用,可以把变量存进去拿出来。
变量在使用时分两步:声明变量和变量赋值
var a //声明变量
a = 3; // 变量赋值
var a = 3;// 声明变量并赋值
= 赋值的意思 ,把后面的值赋给前面的变量
命名规范:
- 由数字、字母、下划线和美元符号($)组成,如age、name
- 严格区分大小写,如app和App是两个变量
- 不能以数字开头,如18age是错误的变量名
- 不能是关键字、保留字,如 var 、 for、 while 等是错误的变量名
- 要尽量做到‘见其名知其意’,如age表示年龄,num表示数字
- 建议遵循驼峰命名法,首字母小写,后面的单词首字母大写,如myFirstName
数据类型
数据类型:
- 值类型(基本类型):
- 字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
- 引用数据类型(对象类型):
- 对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。
P2运算符
算术运算符
加法:+:任何数据类型的值 + 字符串都是字符串
a = “str” + “str” //strstr
b = “str” + undefined // strundefined
c =“str” + 1 + 1 //str11
d = “str” + (1 + 1) //str2
e = 1+1+“str” + (1 +1) //2str2
除法:/:
a = 0/0 ; // NaN -> Not a Number 非数
b = 1/NaN ; // NaN
c = 1/0 ; // Infinity ->正无穷 数字类型
d = -1/0 ; // -Infinity ->负无穷 数字类型
取余:%:
console.log(5%2) ; // 1
console.log(5%3) ; // 2
console.log(0%6) ; // 0
交换值的问题:
var a = 1,b =2;
a、b值交换:
- var c = a ; a = b ; b = c ;
- a = a + b ; // a = 3
- b = a - b ; // 3 - 2 = 1
- a = a - b ; // 3 - 1 = 2
递增递减运算符++, - -
var a = 1 ; a = a + 1 ; => a + +
先打印后运算
var a = 1 ;
console.log(a++) // 1
console.log(++a) // 2
比较运算符
比较:> , < , >= , <= , = = , = = = , ! = , ! =
数字对比 : 就按照数字的来
字符串 - 数字对比 : 先把字符串转换为数字
字符串对比 : 相对应的ASCII码
相等是不看数字类型的
全等是需要看数据类型是否相等的
NaN与包括自己在内任何东西都不相等
逻辑运算符
假: undefined 、null 、NaN 、“” 、0 、false
var a = 1 && 2 && undefined && 10 ;
//遇到真就往后走,
//遇到假或走到最后就返回当前的值
var b = 0 || null || 1 || 0;
//遇到假就往后走,
//遇到真或走到最后就返回当前的值
console.log(a,b); //undefined 1
P3循环
for循环
写法:
for(){
执行语句
}
步骤:
1.声明变量i= 0
2.if ( i < 10 ){
console.log( i )
}
3.i++
4.继续第2步
if ( i < 10 ){ 如果这一步不满足条件,就停止循环
console.log( i )
}
5.继续第3步 i++
==========================================>
可以先把声明变量提出来
var i = 0 ;
for ( ; i < 10 ; ) {
console.log ( i ) ;
i++ ;
}
打印出1 - 10
var i = 1;
for (; i;) {
console.log(i);
i++;
if (i == 11) {
i = 0; // break;
}
}
从 0 开始做加法,加到什么时候 ,总和是小于100的。
var sum = 0;
for(var i = 0; i<100;i++){
sum += i
if(sum >= 100){
break;
}
console.log(i,sum);
}
100以内的数,跳过可以被7整除或个位数是7的数。
for (var i = 0; i <= 100; i++) {
if(i % 7 == 0 || i % 10 == 7){
continue;
}else{
console.log(i);
}
}
打印100里,可以被4, 5, 6, 整除的数
for (var i = 0; i <= 100; i++) {
if (i % 4 == 0 || i % 5 == 0 || i % 6 == 0) {
console.log(i);
}
}
打印0-100的数,()只能有一句,不能写比较,{}不能出现i + + , i - -
for ( 表达式1 ;表达式2;表达式3 ){ }
当表达式2为假(undefined 、null 、NaN 、“” 、0 、false)时,循环结束。中间这个只返回true和false
var i = 101;
for (; i--;) {
console.log(i);
}
10的n次方
var n = 5;
var num = 1
for(var i = 0;i<n;i++){
num *= 10;
}
console.log(num); //100000
n 的阶乘
var n = 5;
var num = 1;
for (var i = 1; i <= 5; i++) {
num *= i;
}
console.log(num); //120
789打印出987
var num = 789;
var a = num % 10;
var b = (num - a) % 100 / 10
var c = (num - a - b * 10) / 100;
console.log("" + a + b + c);
打印三个数中的最大的数
var a = 6,
b = 2,
c = 3;
if (a > b) {
if (a > c) {
console.log(a);
} else {
console.log(c);
}
} else {
if (b > c) {
console.log(b);
} else {
console.log(c);
}
}
打印100以内的质数(仅仅能被1和自己整除的数)
var c = 0;
for (var i = 2; i < 100; i++) {
for (var j = 1; j <= i; j++) {
if (i % j == 0) {
c++
}
}
if (c == 2) {
console.log(i);
}
c = 0;
}
引用值
array、object、function、date、RegExp。
array:
取值:数组取值从0开始
赋值:arr[3] = null
长度:arr.length
取每一位:循环
var arr = [ 1 , 2 , 3 , 4 , 5 , ]
object:
typeof
用来判断类型:number、string、boolean、object ( = 引用类型 object和array =>Object )、function、undefined
console.log(typeof(123)); //number
console.log(typeof('123')); //string
console.log(typeof(true)); //boolean
console.log(typeof({})); //object
console.log(typeof([])); //object
//object = 引用类型 object 和 array =>Object
console.log(typeof(null)); //object 是个历史遗留下来的bug
console.log(typeof(undefined)); //undefined
console.log(typeof(function () {}));//function
//-----------------------------------
console.log(typeof(1-1)); //number
console.log(typeof(1-"1")); //number
console.log(typeof("1"-"1")); //number
//-----------------------------------
console.log(a); //a未被定义 引用报错
console.log(typeof(a)); //undefined
//-----------------------------------
// 判断出来的这个类型的名字再判断,名字肯定是string
console.log(typeof(typeof(12))); //string
console.log(typeof(typeof(a))); //string
显示类型转换
number转换成数字
console.log(typeof (Number('123')) + '-' + Number('123')); //number-123
console.log(typeof (Number('true')) + '-' + Number('true')); //number-NaN
console.log(typeof (Number(true)) + '-' + Number(true)); //number-1
console.log(typeof (Number(false)) + '-' + Number(false)); //number-0
console.log(typeof (Number(null)) + '-' + Number(null)); //number-0
console.log(typeof (Number(undefined)) + '-' + Number(undefined)); //number-NaN
console.log(typeof (Number('a')) + '-' + Number('a')); //number-NaN
console.log(typeof (Number('1a')) + '-' + Number('1a')); //number-NaN
console.log(typeof (Number('3.14')) + '-' + Number('3.14')); //number-3.14
parseInt转换成整型
console.log(typeof (parseInt('123')) + '-' + parseInt('123')); //number-123
console.log(typeof (parseInt(true)) + '-' + parseInt(true)); //number-NaN
console.log(typeof (parseInt(null)) + '-' + parseInt(null)); //number-NaN
console.log(typeof (parseInt(undefined)) + '-' + parseInt(undefined)); //number-NaN
console.log(typeof (parseInt(NaN)) + '-' + parseInt(NaN)); //number-NaN
console.log(typeof (parseInt('3.99')) + '-' + parseInt('3.99')); //number-3
//-----------------------------------进制radix
console.log(typeof (parseInt('10',16)) + '-' + parseInt('10',16)); //number-16(十进制的10的十六进制)
console.log(typeof (parseInt('b',16)) + '-' + parseInt('b',16)); //number-11
//-----------------------------------
console.log(typeof (parseInt('abc123') )+ '-' + parseInt('abc123')); //number-NaN
console.log(typeof (parseInt('123abc') )+ '-' + parseInt('123abc')); //number-123
console.log(typeof (parseInt('1abc23') )+ '-' + parseInt('1abc23')); //number-1
parseFloat:函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。
console.log(parseFloat('3.14')); //3.14
console.log(parseFloat('3.1415926')); //3.1415926
console.log(parseFloat('3')); //3
//-----------------------------------
//3.1415926要小数后两位
var num = parseFloat('3.1465926');
console.log(num.toFixed(2)); // 3.15 、四舍五入的
String
console.log(typeof(String(123))); //string
console.log(typeof(123+'')); //string
console.log('3.14'.toString()); //3.14字符串
console.log(null.toString()); //报错
//-----------------------------------进制radix
console.log( parseInt(100,2).toString(16)); //4
Boolean
undefined 、null 、NaN 、“” 、0 、false,其他都是true
console.log(Boolean(1)); //true
console.log(Boolean(null)); //false
隐式类型转换
var a = '123'; //Number(a)
a++;
console.log(a);
//-----------------------------------
var a = 'a' + 1;
console.log(a); //a1
//-----------------------------------
var a = '3' * 2; // * / - % =>string->number
console.log(a); //6
//-----------------------------------
var a = '1' > 2;
console.log(a); //false
//-----------------------------------
var a = 1 > '2'; // > < >= <=都要转换成number再进行对比,除了下面的一种
console.log(a); //false
//-----------------------------------
var a = 'a' > 'b';
console.log(a);//false
//-----------------------------------
var a = 1 == '1'
console.log(a); //true
//-----------------------------------
var a = 1 === '1' //不进行隐式转换
console.log(a); //false
//-----------------------------------
var a = 1 != '2'
console.log(a); //true
//-----------------------------------
var a = NaN == NaN;
console.log(a); //false
//-----------------------------------
var a1 = 2 > 1 > 3;
var a2 = 2 > 1 == 1;
console.log(a1); //false
console.log(a2); //true
//-----------------------------------
var a1 = undefined > 0;
var a2 = undefined < 0;
var a3 = undefined = 0;
var a4 = undefined == 0;
var b1 = null > 0;
var b2 = null < 0;
var b4 = null == 0;
var c1 = undefined == null;
var c2 = undefined === null;
console.log(a1); //false
console.log(a2); //false
console.log(a3); //0
console.log(a4); //false
console.log(b1); //false
console.log(b2); //false
console.log(b4); //false
console.log(c1); //true
console.log(c2); //false
//-----------------------------------
var num = '123';
console.log(typeof(+ num) + ':' + +num); //number:123
console.log(typeof(- num) + ':' + -num); //number:-123
var num2 = 'abc';
console.log(typeof(+ num2) + ':' + +num2); //number:NaN
console.log(typeof(- num2) + ':' + -num2); //number:NaN
isNaN ->Number(值)->NaN->bool
console.log(isNaN(NaN)); //true
console.log(isNaN(123)); //false
console.log(isNaN("123")); //false
console.log(isNaN("a")); //true
console.log(isNaN(null)); //false
console.log(isNaN(undefined)); //true
do while 循环
无论怎么样,都是要循环一次的。
斐波拉契数列
/**
* n1 n2 n3
* 1 1 2 3 5 8
* n1 n2 n3
* n1 n2 n3
* n1 n2 n3
*/
var n = parseInt(window.prompt('请输入第几位'))
if (n <= 0) {
console.log('输入错误');
} else {
var n1 = 1, n2 = 1, n3;
if (n <= 2) {
console.log(1);
} else {
for (var i = 2; i < n; i++) {
n3 = n1 + n2;
n1 = n2;
n2 = n3;
}
console.log(n3);
}
}
P4函数
一个固定的功能或者是程序段被封装的过程
实现一个固定的功能或者是程序
在这个封装体中需要一个入口和一个出口
入口就是参数,出口就是返回
高内聚低耦合 – > 模块的单一责任制
函数
函数声明(最基本的函数写法)
function test() {
var a = 1,
b = 2;
console.log(a,b);
}
test();
匿名函数表达式 函数字面量
var test = function(){
var a = 1,
b = 2;
console.log(a,b);
}
test();
函数的组成部分:
function 函数名 参数(可选) 返回值() return 。
var aa = parseInt(window.prompt('a'));
var bb = parseInt(window.prompt('b'));
function test(a,b) { //(a,b)占位 -> 形式上占位,形式参数,形参。和实参是一一对应的。
console.log(a+b);
}
test(aa,bb) //实际参数,实参。
形参与实参
function 函数名(形参1、形参2……){
//函数体代码
}
函数名(实参1、实参2……)
- 形参和实参数量可不等
- 在实参里面传了值的,可以在函数内部修改实参的值。如果实参里面没有这个值,给形参赋值,是没有用的。
实参求和
function sum() {
var a = 0;
for(var i = 0 ;i <arguments.length;i++){
a += arguments[i];
}
console.log(a);
}
sum(1,2,3,4)
在函数内部可修改实参的值
function test(a,b) {
a = 3;
console.log(arguments[0]);//3
}
test(1,2)
实参并没有传递,就相当于是个undefined。
function test(a,b) {
b = 3;
console.log(arguments[1]); //undefined
}
test(1);
return
第一个作用:终止函数执行
return在哪个位置,哪个位置就结束。默认是在最后return。
第二个作用:返回值
变量
函数外部:全局变量
函数内部:局部变量
函数体内部可以访问外部的变量,外部的变量不能访问内部的变量。
涉及到 [ [ scope ] ] 的问题
a = 2;
function test() {
var b = 1;
console.log(a); //2
}
test()
console.log(b);//b is not defined
// ----------------------------------------
// a是全局变量 函数内部的是局部变量 [scope]
//内部能访问修改外部,外部不能访问修改内部。
a =1;
function test1() {
var b = 2;
console.log(a); //1
function test2() {
var c = 3;
console.log(b); //2
console.log(a); //1
}
test2();
console.log(c);//c is not defined
}
test1()
function 都有自己的独立作用域
function test1() {
var a = 1;
console.log(b); // not defined
}
function test2() {
var b = 2;
console.log(a); // not defined
}
test1()
test2()
P5
形参b没有赋值,就是undefined。
function test(a = 1, b) {
console.log(a); //1
console.log(b); //undefined
}
test()
给b传入实参, a要选用默认值的话, 就给a传实参传undefined
----------------es5-----------------------------
function test(a,b) {
var a = arguments[0] || 1;
var b = arguments[1] || 2;
console.log(a+b);
}
test()
function test(a, b) {
var a, b;
if (typeof (arguments[0]) !== 'undefined'){
a = arguments[0];
} else {
a = 1;
}
if (typeof (arguments[1]) !== 'undefined') {
b = arguments[1];
} else {
b = 2;
}
console.log(a + b);
}
test()
function test(a, b) {
var a = typeof(arguments[0])!== 'undefined' ? arguments[0] :1;
var b = typeof(arguments[1])!== 'undefined' ? arguments[1] :2;
console.log(a + b);
}
test()
----------------es6-----------------------------
function test(a = 1, b) {
console.log(a); //1
console.log(b); //2
}
test(undefined, 2)
function test(a = undefined, b) {
console.log(a); //1
console.log(b); //2
}
test(1, 2)
递归作业
作业1:定义一个函数,算出n的阶乘,不能用for循环
//n的阶乘 ->不能for循环 -> 递归
//fact(n)
//n! = n*(n-1)! 规律
//出口
function fact(n) {
if(n === 1){
return 1;
}
return n * fact(n-1);
}
console.log(fact(5)); //120
步骤解析
作业2:定义一个函数,算出斐波那契数列的第N位,不能用for循环
// 循环:n3 = n2 + n1;
// 出口:n <= 0 ; n <= 2;
function fb(n) {
if(n <= 2){
return 1;
}
return fb(n-1) + fb(n-2)
}
console.log(fb(5)); //5
步骤解析
预编译流程
1.检查通篇的语法错误
1.5.预编译的过程
2.解释一行,执行一行
函数声明整体提示,变量只有声明提升,赋值是不提升的
test();
function test() {
console.log(1); //1
}
//----------------------------------
console.log(a); //undefined
var a = 10;
console.log(a); //undefined
var a ;
console.log(a); //a is not defined
暗示全局变量 imply global variable:
实际上在全局,写不写var都属于一个东西 window ,window是个系统内置的对象
所以在全局的概念里 a = window.a ; b = window.b。
var a = 1;
console.log(a); // 1
console.log(window.a); //1
b = 1;
console.log(b); //1
// 实际上在全局,写不写var都属于一个东西 window ,window是个系统内置的对象
// 所以在全局的概念里 a = window.a ; b = window.b
// window = {
// a = 1,
// b = 2
// }
在函数内部,没有声明这个变量,而直接给这个变量赋值的话,那它就提升到全局变量,到window里去了
function test() {
var a = b = 1; //步骤:先声明一个a,把1给b,把b的值给a ,b并没有在function内部去声明
}
test();
console.log(b); //1
console.log(window.b); //1
console.log(a); //a is not defined
console.log(window.a); // undefined
AO
预编译的过程:AO : activation object 活跃对象 函数上下文
- 寻找函数的形参和变量声明
- 把实参的参数值赋给形参
- 寻找函数声明,赋值函数体
- 执行函数
function test(a) {
console.log(a); //f a(){}
var a = 1;
console.log(a); //1
function a(){}
console.log(a); //1
var b = function(){}
console.log(b); //f(){}
function d(){}
}
test(2)
/**
* 创建AO对象
* AO = {
* 1. 寻找函数的形参和变量声明
* a:undefined
* b:undefined
* 2.把实参的参数值赋值给形参
* a:undefined -> a = 2
* 3.寻找函数声明,赋值函数体
* a:undefined -> a = 2 -> function a(){}
* function d(){}
* 4.执行函数
* a:undefined ->
* a = 2 ->
* function a(){} ->
* a = 1
* b:undefined ->
* function(){}
* d:function d(){}
* }
*/
//-------------------------------------------
function test(a,b) {
console.log(a); //1
c = 0;
var c;
a = 5;
b = 6;
console.log(b); //6
function b(){}
function d(){}
console.log(b); //6
}
test(1)
/**
* AO = {
* a:undefined->
* a = 1->
* a = 5
* b:undefined->
* function b(){}->
* b = 6
* c:undefined->
* c = 0
* d:function d(){}
* }
*/
GO
GO:global object 全局上下文。 实际上GO === window
var a = 1;
function a() {
console.log(a); //1
}
console.log(a); //1
// GO global object 全局上下文
/**
* GO = {
* 1.寻找变量声明
* a:undefined
* 2.寻找函数声明
* a:undefined ->
* function a(){}
* 3.执行
* a:undefined ->
* function a(){}-> 1
* }
*/
//-------------------------------------------
console.log(a,b); //ƒ a() {} undefined
function a() {}
var b = function(){}
/**
* GO = {
* 1.寻找变量声明
* b:undefined
* 2.寻找函数声明
* a:undefined ->
* function a(){}
* }
*/
AOGO
function test() {
var a = b = 1;
console.log(b);
}
test();
/**
* GO = {
* b : 1,
* }
*
* AO = {
* a:undefined -> 1
* }
*
*/
//-------------------------------------------
var b = 3;
console.log(a);
function a(a) {
console.log(a);
var a = 2;
console.log(a);
function a() {}
var b = 5;
console.log(b);
}
a(1)
/**
* GO = {
* b:undefined, ->3
* a:function a(){...}
* }
* AO ={
* a:undefined
* 1
* function a() {}
* 2
* b:undefined
* 5
* }
*
*/
//-------------------------------------------
a = 1;
function test() {
console.log(a); //undefined
a = 2;
console.log(a); //2
var a = 3;
console.log(a); //3
}
test()
var a;
/**
* GO = {
* a:undefined
* 1
* test: function test(){...}
* }
* AO = {
* a : undefined
* 2
* 3
* }
*
*/
//-------------------------------------------
function test() {
console.log(b);//undefined
if(a){ //不执行
var b = 2;
}
c = 3;
console.log(c); //3
}
var a; //undefined
test();
a = 1;
console.log(a); //1
/**
* GO={
* a:undefined
* test : function test(){}
* c:3
* }
* AO={
* b:undefined
* }
*/
笔试题:
var a = false + 1 ;
console.log(a); //1
//-------------------------------------------
var b = false == 1;
console.log(b); //false
//-------------------------------------------
if(typeof(a) && (-true) + (+undefined) + ''){
console.log('通过了'); //通过了
}else{
console.log('没通过');
}
console.log(a); // a is not defined
console.log( typeof(a) ); //字符串的undefined 真
console.log(-true); //-1
console.log(+undefined); // NaN 也是数字类型
//-------------------------------------------
if(1 + 5 * '3' === 16) {
console.log("通过了"); //通过了
}else{
console.log("未通过");
}
//-------------------------------------------
console.log(!! ' ' + !!'' - !!false || '未通过'); //未通过
AOGO作业:
function test() {
return a;
a = 1;
function a() {}
var a = 2;
}
console.log(test()); //f a(){}
//-------------------------------------------
console.log(test()); //2
function test() {
a = 1;
function a() {}
var a = 2;
return a;
}
//-------------------------------------------
a = 1;
function test(e) {
function e() {}
arguments[0] = 2;
console.log(e);//2
if(a){
var b = 3;
}
var c;
a = 4;
var a;
console.log(b);//undefined
f = 5;
console.log(c); //undefined
console.log(a); //4
}
var a;
test(1);
console.log(a); //1
console.log(f); //5
P6 作用域、作用域链、预编译、闭包基础
AO - > 与function紧密相关 , 相当于是个独立的仓库
函数也是一种对象类型 引用类型 引用值
对象 -> 有些属性是我们无法访问的,js引擎内部固有的隐式属性
[[scope]]
- 函数创建时,生产的一个JS内部的隐式属性。
- 函数存储作用域链的容器,作用域链里面是AO/GO,作用域链就相当于是把这些AO/GO连起来。
- AO/GO
- AO:函数的执行期上下文
- GO:全局的执行期上下文
- 函数执行完成之后,AO是要销毁的
- AO是一个即时的存储容器
- AO/GO
分析图解:
每一个函数的作用域链上都有GO
一个函数都有自己的AO和全局的GO,并且AO排列在GO前
在执行之前(被定义的时候),这个环境和之前是一样的,只有执行了才会改变。
function a() {
function b() {
function c() {
}
c();
}
b();
}
a();
//a定义 a.[[scope]] -> 第0位 GO
//a执行 a.[[scope]] -> 第0位 a - AO
// 第1位 GO
//b定义 b.[[scope]] -> 第0位 a - AO
// 第1位 GO
//b执行 b.[[scope]] -> 第0位 b -> AO
// 第1位 a ->AO
// 第2位 GO
//c定义 c.[[scope]] -> 第0位 b -> AO
// 第1位 a ->AO
// 第2位 GO
//c执行 c.[[scope]] ->第0位 c-> AO
// 第1位 b -> AO
// 第2位 a ->AO
// 第3位 GO
//c结束 c.[[scope]] -> 第1位 b -> AO
// 第2位 a ->AO
// 第3位 GO
//b结束 b.[[scope]] -> 第0位 a - AO
// 第1位 GO
// c.[[scope]] x
//a结束 a.[[scope]] -> 第0位 GO
// b.[[scope]] x
闭包
当内部函数被返回到外部并保存时,一定会产生闭包,
闭包会产生原来的作用域链不释放,
过渡的闭包可能会导致内存泄漏或加载过慢
简单闭包:
function test() {
var n = 100;
function add() {
n++;
console.log(n); //101
}
function reduce() {
n--;
console.log(n); //100
}
return [add,reduce]
}
var arr = test();
arr[0]();
arr[1]();
面包管理器
// 面包管理器
function breadMgr(num) {
var breadNum = arguments[0] || 10;
function supply() {
breadNum += 10;
console.log(breadNum);
}
function sale() {
breadNum --;
console.log(breadNum);
}
return [supply,sale];
}
var breadMgr = breadMgr(50);
breadMgr[0]();
breadMgr[1]();
breadMgr[1]();
星期天计划
//星期天计划管理
function sunSched() {
var sunSched = '';
var operation = {
setSched:function(thing){
sunSched = thing;
},
showSched:function(){
console.log("My schedule on sunday is " + sunSched);
}
}
return operation;
}
var sunSched = sunSched();
sunSched.setSched('studying');
sunSched.showSched();
P7立即执行函数、闭包深入、逗号运算符
前提
function test1() {
console.log(1);
}
function test2() {
console.log(2);
}
test2(); //2
function test3() {
test1();
}
test3(); //1
做一个功能,页面加载完了,接收两个后台传过来的参数,每次加载页面的时候,都求这两个参数的和。
// 做一个功能,页面加载完了,接收两个后台传过来的参数,每次加载页面的时候,都求这两个参数的和。
add(1, 2);
function add(a, b) {
console.log(a + b);
}
console.log(add(1, 2));
function add(a, b) {
return a + b;
}
自动执行,执行完成之后立即释放
立即执行函数 IIFE - Immediately Invoked Function Expression
1.自动执行2.执行完之后就立即销毁
(function(){
内容
})();
(function(){
内容
}()); //W3C建议
(function () {
var a = 1,
b = 2;
console.log(a+b); //3
}());
//----------------------------------
(function (a,b) {
console.log(a+b); //3
}(1,2));
//----------------------------------
var num = (function (a,b) {
return a+b;
}(1,2));
console.log(num); //3
()包裹任何东西都是表达式
一定是表达式才能被执行符号执行,
如果是(表达式的话),加不加名称都一样
逗号运算符,返回最后一个
//逗号运算符,返回最后一个
var num = (2-1,6+5,24+1)
console.log(num); //25
经典案例:
如果要返回0-9 1-10
// 第一种
function test() {
for (var i = 0; i < 10; i++) {
(function () {
document.write(i + ' '); //打印0-9
// document.write((i + 1) + ' ');//1-10
}());
}
}
test()
// //第二种
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function (num) {
document.write((num + 1) + ' ')
}
}
return arr;
}
var myArr = test ();
for (var j = 0; j < 10; j++) {
myArr[j](j);
}
// //第三种
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
(function (j) {
arr[j] = function () {
document.write(j + ' ')
}
})(i);
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j](j);
}
题目:
面试题
//(1,2); //返回的是2 fn()也是
var fn = (
function test1(){
return 1;
},
function test2() {
return '2';
}
)();
console.log(typeof(fn)); //string
//-------------------------------------
var a = 10;
if(function b(){}){
a += typeof(b);
}
console.log(a);
P8闭包高级、对象、构造函数、实例化
闭包
function test1() {
function test2() {
var b = 2;
console.log(a); //1
}
var a = 1;
return test2;
}
var c = 3;
var test3 = test1();
test3();
对象
//对象
var teacher = {
name:'张三',
age:32,
sex:'male',
height:176,
weight:130,
teach:function(){//对象里面叫方法,外面叫函数
console.log('I am teacher');
},
smoke:function(){
console.log(' I am smoking');
},
eat:function(){
console.log('I am eating');
}
}
//访问
console.log(teacher.age); //32
teacher.teach();//I am teacher
console.log(teacher.teach);//f(){ console.log('I am teacher');}
//-------------------------------------
//增加
teacher.address = '北京'
teacher.drink = function(){
console.log(' I am drinking');
}
teacher.drink() //I am drinking
console.log(teacher); //增加了 address
//-------------------------------------
//修改
teacher.height = 180;
teacher.teach = function(){
console.log('I am teacher HTML');
}
console.log(teacher);
console.log(teacher.teach);
//-------------------------------------
//删除
delete teacher.address;
delete teacher.teach; //删除方法, 不用加()
console.log(teacher);
//-------------------------------------
//每smoke体重件减1,每eat,体重+1
var teacher = {
name:'张三',
age:32,
sex:'male',
height:176,
weight:130,
teach:function(){//对象里面叫方法,外面叫函数
console.log('I am teacher');
},
smoke:function(){
teacher.weight--
console.log(teacher.weight);
},
eat:function(){
teacher.weight++
console.log(teacher.weight);
}
}
teacher.smoke(); //129
this
this指的是对象本身
splice和indexOf
splice:
splice() 方法用于添加或删除数组中的元素。
注意:这种方法会改变原始数组。
indexOf:
var num = 3;
var arr = [1,2,3,4]
for(var i = 0;i <arr.length;i++){
if(arr[i] === num){
arr.splice(i,1)
}
}
console.log(arr); //[1,2,4]
//---------------------------------
var num = 5;
var arr = [1,2,3,4]
console.log(arr.indexOf(num)); //-1就代表不在这个数组里面
学生课堂上课案例
var attendance = {
students:[],
total:6,
join:function(name){
this.students.push(name)
console.log(this.students);
},
leave:function(name){
var idx = this.students.indexOf(name);
if(idx !== -1){
this.students.splice(idx,1)
}
console.log(this.students);
}
}
attendance.join('张三')
attendance.join('李四')
attendance.join('王五')
attendance.leave('李四')
var attendance = {
students:[],
total:6,
join:function(name){
this.students.push(name)
if(this.students.length == this.total){
console.log(name + ' 到课,学生已到齐');
}else{
console.log(name + ' 到课,学生未到齐');
}
},
leave:function(name){
var idx = this.students.indexOf(name);
if(idx !== -1){
this.students.splice(idx,1)
}
console.log(name + '早退');
console.log(this.students);
},
classOver:function(){
this.students = [];
console.log('已下课');
}
}
attendance.join('张三')
attendance.join('李四')
attendance.join('王五')
attendance.join('赵六')
attendance.join('吴七')
attendance.join('孙八')
attendance.leave('李四')
attendance.classOver()
创建对象
对象字面量,对象直接量
var obj ={
name:'张三',
sex:'male'
}
obj.name = '李四'
构造函数
1.用系统内自带的构造函数—和对象字面量相等
var obj = new Object();
obj.name = '张三';
obj.sex = '男士';
console.log(obj);
2.自定义构造函数
大驼峰—表面上的区分
// 自定义构造函数
// 大驼峰---表面上的区分
function Teacher() {
this.name = '张三';
this.sex = '男士';
this.smoke = function(){
console.log( 'I am smoking');
}
}
var teacher = new Teacher(); //当new了之后才有this
console.log(teacher);
之间互相不同,改变任意对象的属性和方法与另一个无关
function Teacher() {
this.name = '张三';
this.sex = '男士';
this.smoke = function(){
console.log( 'I am smoking');
}
}
var teacher1 = new Teacher(); //当new了之后才有this
var teacher2 = new Teacher();
teacher1.name = '李四'
console.log(teacher1,teacher2);
function Teacher(opt) {
this.name = opt.name;
this.age = opt.age;
}
var t1 = new Teacher({
name:'张三',
age:12
})
var t2 = new Teacher({
name:'李四',
age:13
})
console.log(t1);
console.log(t2);
P9构造函数及实例化原理、包装类
作业
1.累加器 0;
// 1.累加器 0 闭包
function test() {
var num = 0;
function add() {
console.log(++num); //先自加再打印
}
return add;
}
var add = test();
add();
add();
add();
2.一个班级,学生名字保存在一个数组里,两个方法写在函数中的一个对象里,第一个方法加入班级,第二个方法离开班级,每次加入或离开,都需要打印新的学生名单
function myClass() {
var students = []; //保存的是学生的名字
var operations = { //定义一个对象方法
join:function(name){ //加入
students.push(name);
console.log(students);
},
leave:function(name){ //离开
// for(var i = 0; i < students.length;i++){
// var item = students[i] //优化for循环行能
// if(item === name){
// students.splice(i,1);
// }
// }
var idx = students.indexOf(name);
if(idx !== -1){
students.splice(idx,1)
}
console.log(students);
}
}
return operations;
}
var obj = myClass();
obj.join('张三');
obj.join('李四');
obj.join('王五');
obj.leave('李四')
复习上节课
实例化原理
不执行this不存在,一旦执行了,this就存在
this在没有实例化的时候,指向是windo,一旦实例化了,指向的是对象
原理
function Car(color,brand){
this.color = color;
this.brand = brand;
}
//隐藏了一个 return this;
//所以这个new仅仅是把这个this给造出来了,改变this指向
var car1 = new Car('red','Benz');
console.log(car1.color);
/**
* 1.页面一加载的时候肯定有GO
* GO = {
* Car:(function),
* 3.log执行的时候
* car1:{
* color:'red',
* brand:'Benz'
* }
* }
*
* 2.Car一执行,先保存一个this{}空对象,再往里加
* AO = {
* this:{
* color:color,
* brand:brand
* }
* }
*
* */
new的时候就是系统把原本指向windows的指向了实例化对象
引用值 {} [] function
构造函数当中return的是引用值的时候,就有用,return的是原始值(number,string,undefined)之类的,就没有影响
function Car() {
this.color = 'red';
this.brand = 'Benz';
//return this
// return 123 //red
// return 'abc' //red
// return {} //undefined
// return [] //undefined
}
var car = new Car;
console.log(car.color);
包装类
var a = 1;的时候,原始值是没有自己的方法和属性,就没有a.length啊之类的。
但是当一个数字经过了new之后,就会变成一个对象,及可以设置相应的属性和方法
可以参与运算,对象又回到了原始值
像这种在js里有三种,把原始值转换成对象的方法
new Number
new String
new Boolean
null 和 undefined 是不可以设置任何的属性和方法的。
数组的截断方法
字符串 第一步就是new了一个string,设置length为1,但是abc不为1,就delete删除了
面试题
var name = 'abc'
name += 10; //'abc'
var type = typeof(name); //'string'
if(type.length === 6){ //true
type.text = 'string'; //new String(type).text = 'string' 没地方保存 delete
}
console.log(type.text); //undefined
2.
3.
4.
5.
6.
P10原型、原型链、闭包和立即执行函数
作业:
写一个构造函数,接收数字类型的参数,参数数量不定,完成参数相加和相乘的功能
function Compute() {
var args = arguments,res;
this.plus = function(){
res = 0;
loop('add',res);
}
this.times = function(){
res = 1;
loop('mul',res);
}
function loop(method,res) {
for(var i = 0;i < args.length; i++){
var item = args[i];
if(method === 'add'){
res += item;
}else if(method === 'mul'){
res *= item;
}
}
console.log(res);
}
}
var compute = new Compute(2,4,6);
compute.plus(); //12
compute.times(); //48