一、Js逼格
js是解释型语言:看一行翻译一行(有<>尖括号的都为解释型语言)
优点:跨平台
缺点:速度稍慢c++和c是编译型语言:通篇翻译,c最终生成 .obj文件
优点:快
缺点:不能跨平台Java既不是编译型语言,也不是解释型语言,他有一个Java虚拟机JVM
Java—->javac——>编译———> .class ——->JVM———->解释执行
JS是单线程的
单线程:一个时间只能做一件事(同步:前一个事情做完了,才能做后面一件事)
多线程:一个时间可以做多件事(异步:同时执行几件事)Javascript<====>ECMAscript
- JS三大部分:ECMAScript、BOM、DOM
- 浏览器组成
- shell部分:浏览器上面文件,查看…可以点击的部分
内核部分(1)、渲染引擎:解释html、css等代码
(2) 、 js引擎:解释JS代码<br /> (3)、其他模块
- 主流浏览器及其内核
- IE trident
- Chrome webkit/blink
- firefox Gecko
- Opera presto
-
二、JS基本语法
2.1 变量类型
(1)、原始值: Number(浮点型· ) Boolean String undefined(未赋值的数) null(占位置)
(2) 、引用类型:Array Object function …Date RegExp
<script>// 原始值:stack(栈区-先进后出)var a=1;var b=a;a=10;console.log(a);//10console.log(b);//1// 引用值:heap(堆区)--栈中存放的为堆中的地址,push()方法改变的为原始的arrvar arr=[1,2];var arr1=arr;arr.push(3);console.log(arr);// [1,2,3]console.log(arr1);// [1,2,3]// 引用值:这里是重新为arr2赋值,在堆区中重新开辟了一个存储空间,arr2指向的为新的存储空间,arr3指向的还是原来的存储空间var arr2=[1,2];var arr3=arr2;arr2=[1,3]console.log(arr2);// [1,3]console.log(arr3);// [1,2]</script>
2.2 运算符
```javascript // 运算符
console.log(0 / 0); //NaNconsole.log(1 / 0); //Infinityconsole.log(-1 / 0); //-Infinity/*** 逻辑运算符* undefined,null,"",0,NaN,fakse----->false* 短路逻辑运算符:&& ||*/// && 与运算符,如果前面为真,则直接返回后面的(第二个数|式子),如果第一个为假,则直接返回第一个var y1 = 1 + 1 && 1 - 1;console.log(y1); //0var y2 = 3 && 4;console.log(y2);//4var y2 = 0 && 4;console.log(y2);//0// || 或运算符,如果第一个为真,则直接返回第一个数或式子,第一个为假,直接返回第二个数或式子var h1 = 3 || 4; //4console.log(h1);var h2 = 2 > 1 || 2 > 3;console.log(h2); //truevar h2 = 2 < 1 || 2 ;console.log(h2); //2// !非(先转为boolean值,再非运算)console.log(!123);//falseconsole.log(!"xxx");//falseconsole.log(!"");//trueconsole.log(!!null);//false
<a name="8Bk7u"></a>### 2.3 循环```javascript//var score=parseInt(window.prompt('请输入一个数字'));//var score=parseInt(prompt('请输入一个数字'));var score = +prompt('请输入一个数字');/***1.else--if语句必须是互斥的*/if (score > 90 && score <= 100) {console.log("Alibaba");} else if (score > 80 && score <= 90) {console.log("tencent toutiao meituan");} else if (score > 70 && score <= 80) {console.log("baidu");} else if (score <= 60) {console.log("蘑菇街");} else {console.log("输入错误");}/*** 2.for循环语句* (1).var i=0;* (2).if(i<10){console.log('aaa');}* (3).i++* (4).重复执行(2)和(3)直到:i>=10*/for (var i = 0; i < 10; i++) {console.log('aaa');}var i = 1;for (; i;) {document.write("a__");i++;if (i == 11) {i = 0;}}document.write('<hr/>')for (var i = 0; i < 100; i++) {if (i % 3 == 0 || i % 5 == 0 || i % 7 == 0) {document.write(i + " ")}}var j = 10;for (; j--;) {console.log(j);}document.write('<hr/>')/*** 3.while循环*/var i = 0;while (i < 10) {document.write(i + " ")i++;}document.write('<hr/>')// 逢7打印var k = 0while (k < 100) {if (k % 7 == 0 || k % 10 == 7) {document.write(k + " ")}k++}/*** 4.do...while循环(无论条件是什么都会执行一次)*/document.write('<hr/>')var k = 0do {document.write("哒 ")k++;} while (k < 10)
2.4 数组和对象
1.typeof判断数据类型:number string boolean object undefined function注意:数组也是对象object, null也是对象object2.类型转换2.1null转数字为0,undefined转数字为NaN(但是为数字类型)2.2 parseInt(数,进制{2-36})以‘进制{2-36}’为基底,转为十进制parseFloat2.3 String把任意类型转为字符串2.4 Boolean()把除了 ("",null,undefined,false,NaN,0) 转为true2.5 toString()转为字符串,注意:undefined和null不能用toString()转字符串xxx.toString(2,8,10,16)以10进制为基底,转为目标进制3.隐式转换3.1 isNaN()判断是不是数字,判断之前用Number()转化3.2 ++和--(运算之前用Number()转化)3.3 +和-(都会把运算的值转为数字Number)当+两侧有一个是字符串就调用String(),把两个都转为字符串3.4 * / % - 隐式转换为Number3.5 < > = <= >= (当有一个比较的为数字时,隐式转为数字,当两边都为字符串,比较AISC码值)3.6 == != (Number隐式转换)console.log(1 == "1"); //trueconsole.log(1 == true); //trueconsole.log(false > true);//false (false=0,true=Number(true)=1)console.log(2 > 1 > 3);// 2>1=true=1 1>3=falseconsole.log(undefined==null);//trueconsole.log(undefined===null);//false//不能用NaN去验证NaN(不等)console.log(NaN==NaN);//false//注意:typeof()返回的为字符串console.log(typeof(undefined));//undefined//当使用一个没有定义的变量时,会报错,在typeof中不会报错//经过typeof()判断后会返回一个(字符串)console.log(typeof(aa));//undefinedconsole.log(typeof(typeof(a)));//string
三、函数
1. 代码要求高内聚,低耦合(少的重复,重复部分抽取出来,写为函数) 1. 函数中,形参和实参的个数可以不相同 1. 函数参数—-arguments是,保存所有的实参,是一个数组,不能用forEach遍历 1. 函数名.length为形参长度,arguments.length为实参长度 |
||
|---|---|---|
/*** 1.函数声明*/function test1() {console.log(test1.name);//test1}/*** 2.命名函数表达式*/var test2 = function abc() {console.log(test2.name);//abc}/*** 3.匿名函数表达式-----函数表达式*/var test3 = function () {console.log(test3.name);//test3}test1();test2();test3();console.log(test2);//ƒ abc() {console.log(test2.name);}//console.log(abc);//abc is not defined/*** 4.函数参数---arguments是,保存所有的实参,是一个数组,不能用forEach遍历*/function test4(a,b){console.log(arguments);for(var i=0;i<arguments.length;i++){console.log(arguments[i]);}console.log(test4.length,"形参长度");//2}test4(1,2,3,4,5,6);//Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]function func(a,b){b=3;console.log(b);console.log(arguments[1],"arguments");//3}// 注意:实参有几个,形参就有几个,b和arguments[1]值相等,但是不是同一个东西,有一个映射关系// func(1,2);//3 "arguments"// 实参只有一个,则b为undefined,修改b时,arguments[1],不会再跟着变,应为arguments数组只有【1】func(1);//undefined "arguments"
3.1 函数练习
/*** 3.输入一个数字,取反 并 转为汉字的形式*/function resver(num) {var num=prompt("输入数字");// 注意:从页面传过来的均为字符串,可遍历,取反,倒着遍历var str=''for(var i=num.length-1;i>=0;i--){// str+=num[i]str+=transform(num[i])}console.log(str);}// 把取反的转为汉字function transform(num){switch(num){case "1":return "一";break;case "2":return "二";break;case "3":return "三";break;case "4":return "四";break;}}resver()
3.2 递归
1. 根据给定的题目找规律,列出函数 关系式 1. 根据题目 找到递归的出口(已知条件) 1. 递归:自己调用自己本身(函数) |
||
|---|---|---|
// 1. 阶乘function jie(n) {if (n == 1 || n == 0) {return 1;//递归出口}return jie(n - 1) * n;}console.log(jie(6));// 2. 输出斐波那契数列function f(n) {if(n==1 || n==2){return 1;//递归出口}if(n>2){return f(n-1)+f(n-2)}}console.log(f(5));
四、预编译
**先生成GO,再生成AO,执行函数时,先找自己AO,自己没有再找GO**
| JS三部曲:
1. 语法分析
1. 预编译(解释执行前,先通篇扫描一下,看看有没有语法错误)
(1)、函数声明整体提升 function test(){ console.log(“a”);}
(2)、变量的声明提升 var a; ,变量的赋值不提升 a=1;
3. 解释执行
注意:
1. 任何变量未经声明就赋值为全局变量,a=10;(全局的:window.a=10)
1. 一切声明的全局变量,全是window属性(var a=10;——->window.a=10)
1. window是一个全局的域,var a=10; 相当于 window {a:10}
**预编译发生在函数执行的前一刻:**
1. 创建AO对象(执行上下文)
1. 找形参和变量声明,将变量和形参名作为AO的属性名,值为undefined,var test=function(){} 变量声明
1. 将实参值和形参值相统一
1. 在函数体里找函数声明,值赋予函数体 function test(){ }
全局的预编译:
1. 创建Go对象(window==Go)
1. 同上
| | |
| —- | —- | —- |
|
|
function test() {// 在这里,123先赋值给b,但是b是未经声明的,所以b是全局的,但是var a=b;时,a声明了,是函数局部的//var a=b=123; window{b:123}var a = b = 123;}test();console.log(window.b); //123console.log(window.a); //undefinedconsole.log(b); //123 ---> window.b//console.log(a); //a is not defined,a是局部的,不是window所有/*** AO{ GO{* a:undefined b:123* } }*//*------------------------------------------------------------*//*** 1.创建AO对象* AO{* a:function a() {},* b:undefined,* d:function d(){}* }* 声明已经完成,后面读取过程中,赋值即可*/function fn(a) {console.log(a); //function a() {}var a = 123;console.log(a); //123//函数声明function a() {};console.log(a); //123//变量声明var b = function () {};console.log(b); // function () {}function d() {};}fn(1)/*-------------------------------------------------------*//*** AO{* a:1,* b:function b() {},* c:undefined,* d:function d() {}* }*/function test1(a, b) {console.log(a); //1c = 0;var c;a = 3;b = 2;console.log(b); //2function b() {}function d() {}console.log(b); //2}test1(1);/*-----------------------------------------------------*/function test2(a, b) {console.log(a); //function a(){}console.log(b); //undefinedvar b = 234;console.log(b); //234a = 123;console.log(a); //123function a() {};var a;b = 123;var b = function () {};console.log(a); //123console.log(b); //function (){}}test2(1)/*-------------------------------------------------------------*//*** Go{* a:function a(){}* }*/var a = 123;function a() {}console.log(a, "全局"); //123/*----------------------先GO,再AO-----------------------------*/console.log(mytest); //ƒ mytest() {....}function mytest(mytest) {console.log(mytest); //ƒ mytest() {}var mytest = 234;console.log(mytest); //234function mytest() {}}mytest(1);var mytest = 123;/*-----------------------------函数:先AO,没找到再GO----------------------------*//*** AO{ GO{* a:undefined,* fn:function fn(){....}* } }*/var global = 100;function fn() {console.log(global);}fn();/*-----------------------------------------------------*//*** AO{ GO{* g:undefined g:undefined,* fn:function fn(){....}* } }* AO有自己的g,则不会去找全局的g*/g = 100;function fng() {console.log(g); //undefinedg = 200;console.log(g); //200var g = 300;}fng();var g;/*-----------------------------------------------------------*/function ff() {console.log(b);//undedinedif (a) {var b = 100;//预编译与if语句无关,只看变量声明}c = 234;console.log(c);//234}var a;ff();a = 10;console.log(c);//234
4.1 练习题
/*** 1.str隐式类型转换* 2.false==1是一个判断,结果为boolean值* 3.typeof(a),a没有定义,返回“undefined”,(-true)=-1,* ( +undefined)转为数字为NaN,但(+""),则&&两边均为字符串,变为boolean值为true* 4."11" * 2转为数字* 5.(!!" ")字符串转为boolean为true,这不是空串,(!!"")空串转为false,!!false还为false* ‘||’两边有一个为true,后面不在执行*/var str = false + 1;console.log(str);//1var demo = false == 1;console.log(demo);//falseif (typeof (a) && -true + (+undefined) + "") {console.log("精彩知识");//精彩知识}if (11 + "11" * 2 == 33) {console.log("精彩知识");//精彩知识}!!" " + !!"" - !!false || console.log("不能打印哦");
五、作用域和作用域链


/*** a先被定义,a.[[scope]]中存在一个全局的执行上下文 GO* a被执行时生成一个a的执行时期上下文,AO,放在作用域链a.[[scope]]的顶端* a执行时b被创建,b.[[scope]]中包含的是a()的AO和GO* b被执行时产生b自己的执行上下文,放在b.[[scope]]的顶端*/function a() {function b() {var b = 123;}var a=123;b();}var glbo=100;a();
5.1 闭包
| 闭包:当内部函数被保存到外部时,将会生成闭包。(不论是return 还 把函数赋值给全局变量) 危害:闭包会导致原有作用域链不释放,造成内存泄漏—->占内存空间 作用:(1).实现共有变量 eg:累加器 (2).可做缓存(存储结构) eg:eater (3).实现封装,属性私有化 eg:Person() (4).模块化开发,防止污染全局变量 |
||
|---|---|---|
/*** 1. a定义和执行时a.[[scope]]产生--> aAO 和 GO,a执行时把b的副保存到了demo,此时a执行完切断与aAO连线,不会切断其他函数与aAO连线* 2. b定义时的环境为a执行时环境:b.[[scope]]产生--->aAO 和 GO(b引用aAO,但a执行完,他与的aAO的线已切断)* 3. b执行时产生自己的上下文bAO,当b执行完成后bAO释放,再次执行时再生成一个新的bAO* 但是每一个bAO都引用的为aAO中的变量,aAO没有释放*/function a(){var num=100;function b(){num++;console.log(num);}return b;}var demo=a();demo();//101demo();//102var demoaa;function tt() {var abc = 100;function a() {console.log(abc);}demoaa = a;}tt();demoaa();
1
/*** 1. a定义和执行时a.[[scope]]产生--> aAO 和 GO,a执行时把b的副保存到了demo,此时a执行 完切断与aAO连线,不会切断其他函数与aAO连线* 2. b定义时的环境为a执行时环境:b.[[scope]]产生--->aAO 和 GO(b引用aAO,但a执行完,他与 的aAO的线已切断)* 3. b执行时产生自己的上下文bAO,当b执行完成后bAO释放,再次执行时再生成一个新的bAO* 但是每一个bAO都引用的为aAO中的变量,aAO没有释放*/function a() {var num = 100;function b() {num++;console.log(num);}return b;}var demo = a();demo(); //101demo(); //102/*** 2.闭包实现累加器*/function add() {var count = 0;function demo() {count++;console.log(count);}return demo;}var counter = add();counter(); //1counter(); //2counter(); //3counter(); //4counter(); //5/*** 2.闭包可以实现缓存(1)*/function test() {var num = 1;function a() {num++;console.log(num);}function b() {num--;console.log(num);}return [a, b]}var myArr = test();// 他们都在操作test里面的num,a()和b()用的闭包时同一个testAO,连线都在testAOmyArr[0](); //2myArr[1](); //1/*** 闭包可以实现缓存(2)*/function eater() {var food = "";var obj = {eat: function () {console.log('我喜欢吃' + food);food=""},addFood: function (myfood) {food = myfood}}// 对象obj里面的两个函数,也和food形成了闭包,他们可以操作eaterAO中的foodreturn obj;}var eater1 = eater();eater1.addFood("苹果");eater1.eat();//我喜欢吃苹果
5.2 立即执行函数
1. 立即执行函数:此函数没有声明,在一次执行过后立即释放。适合做初始化工作(只能执行一次) 1. 立即执行函数可以有 参数 和 返回值 1. 立即执行函数 也有执行时期上下文,可进行预编译 1. 模式:(function(){ }());—->W3C模式 (function (){ })(); 1. 只有函数表达式才能被执行符号()执行 1. 被执行符号()执行的表达式,他的名字会被自动忽略—>undefined(表达式)/xxx is not defined(声明转表达式) 1. ‘ + / - / ! ‘可以把一个函数声明转为一个函数表达式,用执行符号执行 1. 立即执行函数原理: - function test(){} 是一个函数声明,当用括号括起来后变为函数表达式,用执行符号直接执行 - (function (){}()) 这样写是因为数学运算先看外面大括号,执行符号放在里外是一样的 - 表达式执行后函数名字被忽略,所以写不写名字test效果一样 |
||
|---|---|---|
/*----------------------------(1):函数声明------------------------------------*/function test1() {console.log("aaaa");//}()---->这样写是报错的,这是执行一个函数声明}test1(); //这样可执行,这是执行一个函数表达式;test==123.../*--------------------------(2):函数表达式-----------------------------------*/var test2 = function () {console.log("bbb");}() //可立即被执行,这是一个匿名函数表达式console.log(test1 + "----test1"); //function test1(){...}console.log(test2 + "----test2"); //undefined--->此时变为 var test2;/*----------------------(3): + - ! 把声明转为表达式-------------------------*/+ function test3() {console.log("aaaa");}();console.log(test3 + "----test3"); //test3 is not defined/*---------------------(4):执行原理---------------------------------*/(function test4(){console.log("111234");})();
5.3 闭包和立即执行函数问题
注意:函数执行完了会被销毁不是函数本身不见了,而是切断了与作用域链 AO 之间的联系
(1).for循环产生十个匿名函数表达式,被保存在test()函数的外部,此时这十个函数表达式分别和test()形成了闭包,有同一个testAO,他们共用一个i,且i在for循环后值变为10,因此在调用这十个函数时,他们访问的都为testAO**里面的i,且i=10;所以会打印十个10**
function test() {//一个数组里面有10个函数 arr[function(){},function(){}...]var arr = [];for (var i = 0; i < 10; i++) {//for循环执行十次,产生十个独立的函数,这十个函数分别和test()形成闭包,共用test()里面的i//当test()执行完才会执行arr[i]绑定的函数,此时i已经变为10arr[i] = function () {console.log(i);}}return arr;}var myArr1 = test();for (var j = 0; j < myArr1.length; j++) {myArr1[j]();}
(2).若想要不是每次都输出十个10,而是输出1~9,则需要借助立即执行函数来实现,即for循环产生十个立即执行函数,立即执行函数入参为i,且立即执行函数又和arr[i]=function(){…}形成了闭包,此时arr[i]执行时使用的i为立即执行函数中的i,这个i为产生立即执行函数时,传入的参数,他们分别为1~9,所以输出结果1~9(其实此时的作用域链为:arr[i]AO—立即执涵AO—testAO—GO)
function test() {//一个数组里面有10个函数 arr[function(){},function(){}...]var arr = [];for (var i = 0; i < 10; i++) {/*** 1. for循环执行十次,产生十个立即执行函数,这十个函数分别和test()形成闭包* 2. 立即执行函数是立即执行的,把for循环当前i作为参数,执行立即函数里面的arr[i]赋值语句,产生函 数表达式* 3. 每个立即函数里面的arr[i]=...和立即执行函数执行闭包,使用立即执行函数里的 参数i*/(function (j) {arr[j] = function () {console.log(j);}})(i);}return arr;}var myArr1 = test();for (var j = 0; j < myArr1.length; j++) {myArr1[j]();//1 2 3 4 5 6 7 8 9}
六、对象、包装类
6.1 对象
|
1. 当删除(delete obj.xxx)一个对象属性后,该属性值为undefined
1. 对象的定义(this指的是当前对象)
(1).字面量形式:var obj={name:”xx”,age:20,…}
(2).自定义构造函数:function Student(name,age){ this.name=name, this.age=age}
3. 构造函数内部原理:
(1).在函数体最前面隐式加上函数体 this={}
(2).执行自家写的 this.name=name,this.age=age ,把他们加入this对象
(3).在函数体结尾隐式返回:return th**is**;
(注意:如果我们自己显示的在函数体结尾返回一个 对象或数组,则new出的值为 对象为数组;如果自己 显示返回表达式/变量,则返回的为隐式的this) | | |
| —- | —- | —- |
|
|
/*------------------------(1).字面量创建对象——-----------------------------*/var obj={name:"小希",age:20,myhobby:"",eat:function(){console.log("我爱吃饭");},like:function(hobby){this.myhobby=hobby;console.log("我喜欢---"+this.myhobby);}}console.log(obj.name+"---before");obj.gender="女";obj.like("足球");obj.age=30;delete obj.name;//当删除一个对象属性后,该属性值为undefinedconsole.log(obj.name+"---del");console.log(obj);/*------------------------(1).自定义构造函数创建对象——-----------------------------*/function Person(name, age) {//1.当new一个对象隐式 var this={}//2. AO{name:"",age:""}this.name = name;this.age = age;//3.隐式返回:return this;// return {per:"人"}// return [1,2,3]// return 123;}// var p = Person("张三", 22); //p为undefinedvar p = new Person("张三", 22);console.log(p);
6.2 包装类
1. 只有对象才有属性和方法,new Number(123),new String(‘www’),new Boolean(true)这样new出来的都是对象,可以添加属性和方法,number类型对象可以参与运算(number2),但运算完后又变成了表达式形式(number类型) 1. 注意:undefined和null不可有属性和方法 1. *字面量形式的number,string,boolean值是一定没有属性和方法的,但是我们给他们添加属性和方法也不会报错,但访问的时候,添加的属性值为undefined———>系统帮我们隐式转换了包装类 1. 包装类:new Number() new String() new Boolean() |
||
|---|---|---|
var num = 123;//1.系统帮我们把字面量包装:new Number(123).len=4; 执行完这一步后,这个对象立即deldet销毁num.len = 4;//2.访问时包装:new Number(123).len;这个new出来的对象与上面不同,上面被销毁了,新的里面没有len 属性console.log(num.len); //undefinedconsole.log(num.lengtn); //undefinedvar str = "abxc";//系统包装类:new String('abxc').length=2;但执行后就立即销毁了,所以再访问str.length仍为真实长度str.length=2;console.log(str);//abxc// 字面量没有属性和方法,但是length属性是字符串对象自带的,转为:new String("abxc").length=4console.log(str.length); //4
七、原型、原型链



Person1.prototype={name:'a',//sayName里面的方法,谁调用他,this就是谁sayName:function (){console.log(this.name);}}function Person1(){this.name='b'// console.log(per1.name);----->b}var per1=new Person1();console.log(per1.name);//a Person1自身没有name时
|
1. var obj={}——>系统自动转换new Object()
1. var obj=new Object()两者效果相同,都有原型,建议使用字面量形式创建对象
1. obj.proto———->Object.prototype(原型指向)
1. 绝大多数对象都会继承自 Object.prototype——————->不是所有
1. 创建对象另一种方式:var p=Object.create(原型|null),当里面为null时,是没有原型的;当为原型时,继承原型
| | |
| —- | —- | —- |
|
|
7.1 toString()方法
var num=123;console.log(num.toString());//"123" new Number(num).toString()//num的原型 Number.prototype----->Number.prototype.__proto__----->Object.Prototype(原型链)//Number.prototype本身有toString()属性,是对从Object.Prototype继承来的toString()的重写//Number.prototype={toString:function(){ }}
7.2 call/apply(重点)
7.2.1 call
/*function test(){}test();---------------------->实质:test.call()*/function Person1(name, age) {//this==obj call改变了this的指向,此时利用Person构造函数,obj既有name又有age(利用别人的方法实现自己的功能)this.name = name; //obj.name=namethis.age = age; // obj.age=age}var person1 = new Person1("小", 12);var obj = {}Person1.call(obj, '李', 10); //没有new的时候this是window,call之后this变成了objconsole.log(obj); // {name:"李",age:10}/*---------------------------------------------------------------------------------------*/function Student1(name, age) {//构造函数隐式在函数体前面加:var this={}this.name = namethis.age = age}function Student2(name, age, phone, gender) {//构造函数隐式在函数体前面加:var this={}//利用call改变指向,利用Students实现一部分功能,相当于在this中添加属性Student1.call(this, name, age)this.phone = phone;this.gender = gender;}var s = new Student2("桂", 21, "110-1101", '男');// Student1.call(s,"桂",21);console.log(s);/*---------------------------------------------------------------------------------------*/function Wheel(wheelSize,style){this.wheelSize=wheelSizethis.style=style}function Sit(c,sitColor){this.c=cthis.sitColor=sitColor}function Model(height,width){this.width=widththis.height=height}function Car(wheelSize,style,c,sitColor,height,width){Wheel.call(this,wheelSize,style)Sit.call(this,c,sitColor)Model.call(this,height,width)}var car = new Car(100,"big","like","red",200,700)console.log(car);
7.2.2 apply
| apply与call基本相同,apply在传参数时传一个arguments(一个实参数组) call在传参数时,实参按照形参个数一个一个去传 |
||
|---|---|---|
function Wheel(wheelSize,style){this.wheelSize=wheelSizethis.style=style}function Sit(c,sitColor){this.c=cthis.sitColor=sitColor}function Model(height,width){this.width=widththis.height=height}function Car(wheelSize,style,c,sitColor,height,width){Wheel.apply(this,[wheelSize,style])//前提是必须 new Car()这个对象,否则this为window无意义Sit.apply(this,[c,sitColor])Model.apply(this,[height,width])}var car = new Car(100,"big","like","red",200,700)console.log(car);
7.3 继承的演变
7.3.1 继承原型链
7.3.2 call/apply改变指向的继承
7.3.3 共享原型继承
Person2.prototype.lastName = "桂"function Person2() {}function Son() {}// 封装函数实现继承(A,B)是A继承Bfunction extend(Target, Origin) {Target.prototype = Origin.prototype}extend(Son, Person2);var son = new Son();console.log(son.lastName); //'桂'var person2 = new Person2();console.log(person2.lastName); //'桂'/*** 弊端:不能给Son的原型添加自己的方法,给Son添加Persona2也存在,他们共享原型,指向同一个空间* 解决:圣杯模式*/
7.3.4 圣杯模式继承
function inherit(Target, Origin) {function F() {}F.prototype = Origin.prototypeTarget.prototype = new F()//当形成继承链c.__proto__---->new F().__proto__--->Father.prototype后,c.constructor变成f Father(){}//把c.constructor的构造函数原型改回来Target.prototype.constructor=Target//获取Target真正继承自谁的信息Target.prototype.uber=Origin.prototype}Father.prototype.name = "哈哈"function Father() {}function Child() {}inherit(Child, Father);var c = new Child();var f = new Father();console.log(c.name, f.name); //哈哈 哈哈Child.prototype.male = "女"console.log(c.male, f.male); //女 undefined
7.4 命名空间
| 闭包的第四点用途-—-命名空间:模块化开发,防止全局污染问题 两种方案:(1).对象里面放对象 (2).闭包 |
||
|---|---|---|
var name="all"var myGxt=(function(){var name="gxt"function callName(){console.log(name);}//立即执行函数,时自己立即执行的,且里面的变量形成了私有空间,不会与外部的变量冲突,在立即执行函数完成立即销毁return function(){callName();}}())var myFxh=(function(){var name="fxh"function callName(){console.log(name);}//立即执行函数,时自己立即执行的,且里面的变量形成了私有空间,不会与外部的变量冲突,在立即执行函数完成立即销毁return function(){callName();}}())myGxt();//gxtmyFxh();//fxh//当许多人一起开发时,可能出现变量的命名冲突,解决办法:命名空间(闭包)/* var obj={department1:{gxt:{name:"hhh",age:123}},department2:{fxh:{name:"xxx",age:30}}}var gxt=obj.department1.gxt;console.log(gxt.name)这样使用,不会有全局污染,但是使用麻烦,可以用更简单的闭包*/
7.4.1 实现函数的连续调用
//实现方法的连续调用,return this;在对象中,this指当前对象dengvar deng={smock:function (){console.log("吸烟");return this;},drink:function (){console.log("喝酒");return this;},perm:function (){console.log("烫头");return this;}}deng.smock().drink().perm();
7.4.2 属性调用两种方式
等同于 this.wife—->默认转为this[“wife”]
// 属性的拼接问题var wife={wife1:{name:"a"},wife2:{name:"b"},wife3:{name:"c"},wife4:{name:"d"},// 传过来一个num,根据num来打印数据chooseWife:function(num){console.log(this['wife'+num]);//等同于 this.wife--->默认转为this["wife"]}}wife.chooseWife(1)
7.4.3 for —in—遍历对象的注意点
1. for key in obj 遍历的时候,必须使用obj[key],因为obj.key底层转为obj[‘key’],系统认为访问的时key这个属性,因此打印undefined 1. for—in—会遍历原型中的属性,但指会打印自己的原型,如:Object.property系统中的原型中属性不会打印 |
||
|---|---|---|
7.4.4 hasOwnProperty()和in对象都有
1. hasOwnProperty(),用来判断对象的属性是否属于自己,会过滤出原型链中的属性不打印 1. in可以判断原型中的属性返回true 1. hasOwnProperty可判断原型中属性返回false |
||
|---|---|---|
var testObj={name:"gxt",age:12,gender:"男",__proto__:{//一般原型中的属性也可用for..in..遍历,不想打印原型中属性,用hasOwnProperty过滤lastName:"fxh"//__proto__指向 Object.prototype}}Object.prototype.abc="123"for(var key in testObj){if(testObj.hasOwnProperty(key)){console.log(testObj[key]);//此时不打印lastName}}for(var key in testObj){if(!testObj.hasOwnProperty(key)){console.log(key+"---"+testObj[key]);//此时不打印lastName和abc}}
7.4.5 instanceOf
1. A instanceOf B:判断 A对象 是不是 B构造函数构造出来的 1. 看A原型链上 有没有 B原型 |
||
|---|---|---|
function Perso() {}var pp = new Perso()console.log(pp instanceof Perso); //trueconsole.log(pp instanceof Object); //trueconsole.log([] instanceof Object); //trueconsole.log([] instanceof Array); //true
7.4.6 识别{}或[]的三种方式



Object.prototype.toString.call([])Object.prototype.toString=function(){//toString是一个函数,里面有this,谁调用这个函数,this就是谁//对象调用,this就是Object---->一定会使用this//1.识别this//2. 返回相应结果}Object.prototype.toString.call([])//这方法的执行,改变了toString里面的this指向,使其指向Arrayobj.toString();//这时toString里面的this时obj
八、this指向问题
1. 预编译过程中,this指向window 1. 全局作用域里this—->window 1. call/apply可以改变函数运行时this指向 1. obj.func(); func(); 里面的this指向obj,谁调用函数,函数里this指向谁 |
||
|---|---|---|
九、arguments
9.1 arguments.callee
arguments.callee指向函数自身的引用,eg:立即执行函数是没有函数名的,但递归函数的使用必须要函数名,因此可以使用arguments.callee来解决 |
||
|---|---|---|


var num=(function(n){if(n==1){return 1;}return n*arguments.callee(n-1)}(20))console.log(num);//2432902008176640000
9.2 caller
caller不是arguments的属性,arguments只有callee和length属性;caller是函数自己的属性
//在哪个环境调用demo函数,这个caller就是谁function test(){demo();}function demo(){console.log(demo.caller);}test();//f test(){ demo(); }
十、浅拷贝和深拷贝
10.1 浅拷贝
//不管属性的类型是什么,直接通过key--value一次性拷贝过来var obj = {name: '罗迹',age: 20,like: '许沐',eat:['苹果','葡萄','香蕉']}var objCopy = {}// 实现浅克隆function copy(origin, target) {//如果没有传第二个参数时var target = target || {};for (var key in origin) {target[key] = origin[key]}return target;}console.log(copy(obj, objCopy));
10.2 深拷贝
var obj = {name: '罗迹',age: 20,like: '许沐',eat: ['苹果', '葡萄', '香蕉'],hobbit: {name: "哈哈",gender: '男',xxx: ["a", "b", "c"]}}var objCopy = {}// 深克隆/*** 1.先判断是不是原始值,还是引用值,{}和[]tyoeof都为object* 2.hasOwnProperty判断是不是原型链上的属性4* 3.判断每一个object类型的是{}还是[],利用Object.prototype.toString.call([])==[onject Array]* 4.对于{}和[]递归调用*/function deepCopy(origin, target) {var target = target || {};var toStr = Object.prototype.toString;for (var key in origin) {// 属于该对象自身的属性才继续下一步判断if (origin.hasOwnProperty(key)) {// 如果为引用值,继续判断if (origin[key] !== null && typeof (origin[key]) == "object") {if (toStr.call(origin[key] == "[object Array]")) {target[key] = []} else {target[key] = {}}// 递归调用deepCopy(origin[key], target[key])} else {// 原始值,直接赋值target[key] = origin[key]}}}return target;}console.log('深克隆', deepCopy(obj, objCopy));
十二、三目运算符














