作者声明:本博客中所写的文章,都是博主自学过程的笔记,参考了很多的学习资料,学习资料和笔记会注明出处,所有的内容都以交流学习为主。有不正确的地方,欢迎批评指正
本节课学习视频来源:https://www.bilibili.com/video/av26084949
JavaScript高级
课程内容介绍
1.1值类型与引用类型及参数传递
值类型与引用类型
变量的内存的存储结构
值类型和引用类型赋值与传递
示例代码
<script> var a = 9,b; var c = { age:9 },d; // 把a值类型复制给了b b = a; b = 19;//修改了b的值 console.log( a ); //a => 9 console.log( b ); //b => 19 //如果是引用类型的赋值后操作 d = c; //把c引用类型赋值给了d d.age = 22;//留给了d变量的age属性为22 //那么 c的age属性是多少呢? console.log( c.age ); //c.age => 22 console.log( d.age ); //d.age => 22 </script>
示意图:
函数参数的值传递和引用传递
示例代码
<script> var a = 9; // 简单类型,值类型 var b = { name:"laoma", age:18 }; //引用类型 //把 a 和 b 分别传给 c1 c2 //函数参数: //如果是简单类型,会作为一个值类型的数值副本传到函数内部 //如果是一个参数是引用类型,会将引用类型的地址值赋值给传入函数的参数 function demo( c1,c2 ){ c1 = 29; //对c1做了更改 c2.name = "6666"; //更改了c2引用类型的属性name } // 调用函数执行 demo( a,b ); console.log( a ); //a => 9 console.log( b ); //b.name => "6666" </script>
示例代码
<script> function max( a,b ){ console.log( arguments ); //每个函数内部都可以直接访问arguments,里面就是存放着我们传递给函数的参数 //arguments不是一个数组 但是跟数组类似 //arguments有一个length属性,属性值就是传递 实参 数的个数 for( var i=0;i < arguments.length;i++ ){ console.log( arguments[i] ); //打印所有的参数 } if( a > b ){ return a; }else{ return b; } } console.log( max.length ); //函数的length属性是指 形参 的个数 var t = max( 9,10 ); console.log( t ); var m = max( 9,10,20,30 ); </script>
示例代码:
//实现max方法,可以接受任意多个参数,返回这些参数中最大的那个值 function myMax(){ // 如果调用函数的方法没有传递参数,那么直接返回NaN if( arguments.length <= 0 ){ return NaN; } var max = arguments[0]; for( var i = 0;i < arguments.length;i++ ){ if( arguments[i] > max ){ max = arguments[i]; } } return max; } var m = myMax( 10,9,2,33,22,18 ); console.log( m ); // m => 33
示例代码:
<script> //函数参数的封装 //一个函数:封装一个矩形 //矩形:x y坐标 width height 背景色 文字信息 文字坐标 文字字体 颜色 //全部用新参来处理矩形的参数 function rect( x,y,width,height,bgColor,text,text_color,text_x,text_y ){ } // 如果函数的形参非常多 会有如下问题 // 1 开发人员很难记忆形参的具体参数 // 2 传递参数的时候,如果顺序不小心写错了,那么会导致函数内部出现错误 // 3 编写 代码不方便 //解决办法:把这些参数封装成一个对象进行传递 function rect2( obj ){ //拿到矩形的 x y 坐标 console.log( obj.x + " " + obj.y ); } //rectObj var rectObj = { x:19, y:20, 200, height:300, bgColor:"#ccc", text:"laoma" } rect2( rectObj ); </script>
1.2函数高级内幕
JavaScript时间循环机制
执行上下文相关的概念
执行上下文的执行栈
示例代码
<script> //代码执行之前的时候,就会立即创建一个全局的执行上下文 Global Excution Context //创建完了全局的执行上下文之后,把全局执行上下文压如 执行环境栈中 function f1(){ console.log( "f1" ); } function f2(){ console.log( "f2" ); f3(); } function f3(){ console.log( "f3" ); f4(); } function f4(){ console.log( "f4" ); } f1(); f2(); </script>
分析
示意图
执行上下文的声明周期
示例代码:
<script> //变量声明 var a1 = 19, a2 = 20, a3 = "sss", b1 = { name:'laoma' }; //函数调用 a1 = f1( a1,a2 ); //函数声明 function f1( a,b ){ //f1函数的执行上下文 //第一步:扫描参数 a = 19 b = 20 //第二步:扫描函数声明 f2 = function(){} //第三步:扫描变量声明 t = undefined m = undefined i = undefined var t = 0, m = 10; for( var i = 0;i < a;i++ ){ console.log( i ); } function f2(){ console.log( f2 ); } return a + b; } </script>
示意图
JavaScript的解释和执行阶段
函数变量的作用域
示例代码
<script> var t = 9; //全局作用域,全部都可以以访问 function f1() { //f1函数 全局作用域 var t2 = 10; //t2 是f1函数内部可访问 console.log(t); function f2() { //f2函数式 f1函数的做哟用于 var t3 = 200; //t3 只能在f2函数内部访问 console.log(t2); return t2 * t2;//f2函数可以访问f1函数的作用域的变化量 及 f2 自己内部的变量 } return f2(); } var m = f1(); console.log(m); </script>
示意图
没有块级作用域
示例代码
<script> //没有块级作用域 function f1(){ var t1 = 9; //只能在f1函数内部访问到 for( var i = 0;i < 10;i++ ){//i 变量在 for 循环中定义中的 console.log( i ); } console.log( i ); //因为js没哟块级作用域 所以可以直接访问i变量 } f1(); console.log( t1 );//全局是访问不到f1函数内部的变量的 </script>
变量提升
示例代码:
<script> var a = 10; //全局变量,全局都可以访问 //先执行f1函数,然后再定义f1函数的内容 f1(); function f1(){ //函数的变量提升:因为在函数执行之前,先创建了函数的EC,在创建EC的时候 //已经把函数里面成名的变量都已经初始化成了undefined //所以:hositing存在 //很多人有这样的习惯,把函数内部所有的变量声明都放在函数的头部 console.log( a ); //a => undefined var a = 19; //给局部变量a赋值19 console.log( a ); //a => 19 //特殊情况:变量声明和函数声明 同时拥有一个名字的时候,函数优先级高 console.log( b ); // b => function var b = 9; //因为js是动态语言,把b重新赋值9,把之前的function覆盖 function b(){ } console.log( b ); // b => 9 } console.log( a ); //a => 10 </script>
window
示例代码
<script> //在浏览器中 全局对象就是window //在全局作用域中声明 的变量和函数声明都会作为window的属性存在 var a = 9; function b(){ console.log( a ); } console.log( "a" in window ); console.log( window.a ); console.log( window["a"] ); console.log( window.b() ); console.log( window.b ); </script>
变量提升案例
示例代码案例1
<script> //案例1 var a = 18; //全局变量 function d(){ console.log( a ); var a = { age:19 }; console.log( a ); } d(); //输出undefined 输出对象 console.log( a ); //a => 18 </script>
示例代码案例2
<script> //案例2 //比较 if( !("a" in window) ){ //"a" in window => true var a = 1; } console.log( a );//a => undefined //比较 if( !!("a" in window) ){ var a = 1; } console.log( a );//a => 1 </script>
示例代码案例3
<script> //案例3 console.log( a );//a => function var a = 20; //对a进行重新赋值 console.log( a );//a => 20 function a(){ //创建 GEC 函数优先级高于变量优先级 } </script>
示例代码案例4
<script> f(); console.log(a); //a未声明 异常 console.log(b); //b => 9 console.log(c); //c => 9 function f(){ var a = b = c = 9; //a 局部 bc 全局 //var a = 9,b = 9 ,c =9;//定义局部变量 console.log( a ); //a => 9 console.log( b ); //b => 9 console.log( c ); //c => 9 } </script>
示例代码案例5
<script> //案例 5 f(); function f(){ for( var k = 0;k < 10;k++ ){ console.log( k );//0-9 } console.log( k ); //10 js没有块级作用域 } //结果 0-10 </script>
作用域链
示例代码
函数四种调用模式与this
示例代码 方法调用模式
<script> //定义构造函数 //定义一个Dog类的构造函数 function Dog( dogName ){ //如果函数当做构造函数来用 //第一步:创建一个空对象(新对象),函数上下文===this //第二步:把空对象赋值给函数上下文,this = 新对象 this.name = dogName; this.age = 0; this.run = function(){ console.log( this.name + " is running..." ); }; //如果函数当做构造函数调用,并没有返回任何数据的时候,默认就会返回新对象 this } //使用构造函数创建一个Dog类型的实例 var d = new Dog( "lddd" ); //调用d对象实例的run()方法,这就是对象的方法调用模式 //在方法调用模式中,方法内部的this指向当前调用者的对象 => d //this === d//true d.run(); </script>
示例代码 构造器调用模式
<script> //构造器调用模式 构造函数 //关键字new function Cat(){ //第一步:创建一个空对象(新对象) //第二步:给函数上下文赋值 新对象 this = 新对象 this.age = 19; this.name = "cat";//在构造函数内部定义的this属性 this.run = function(){ console.log( this.name + "run..." ); } //如果构造函数没有返回值,那么久返回this(新对象) // return 3;//即使有返回值,如果返回值类型是简单类型,那么会被忽略 // return null; //如果返回值是一个引用类型(去掉null)那么新对象就会被抛弃,把这个引用类型返回 return { name:9999, run:function(){ console.log( "自己返回的{}" ); } };//return 了一个对象的字面量 return 了一个因信用类型 } var cat = new Cat();//构造函数调用模式 //如果使用new关键字+构造函数执行的话 触发了构造函数执行模式 cat.age = 20; cat.name = "2222"; cat.run();//方法调用模式 </script>
示例代码 函数调用模式
<script> //函数调用模式 function f( a,b ){ console.log( a + " " + b ); this.a = 19; //this === window true console.log( "a" in window ); console.log( this ); //window 全队对象 严格模式undefined } f( 2,3 );//直接调用函数:f() 函数调用模式 function Dog(){ this.age =19; console.log( this ); } Dog();//函数执行模式 => window var d = new Dog();//构造函数执行模式 => d对象 </script>
示例代码 apply call
<script> //函数调用模式 function f( a,b ){ console.log( a + " " + b ); this.a = 19; //this === window true console.log( "a" in window ); console.log( this ); //window 全队对象 严格模式undefined } f( 2,3 );//直接调用函数:f() 函数调用模式 function Dog(){ this.age =19; console.log( this ); } Dog();//函数执行模式 => window var d = new Dog();//构造函数执行模式 => d对象 </script>
函数四种调用模式的案例
示例代码:
<script> //1 定义按钮类 ,要求按钮类的构造函数可以接受参数初始化按钮的宽度 高度坐标xy function Btn( width,height,x,y ){ //构造函数内部初始化值 this.width = width; //给this对象上的width属性赋值 width参数的参数值 this.height = height; this.px = x; this.py = y; } var b = new Btn( 100,100,30,30 ); //2 借用Math的min方法实现求数组[2,9,33]中的最小值 // var m = Math.min( 2,9,33 ); // console.log( m ); // var m = Math.min.apply( null,[2,9,33] ); // console.log( m ); var arr = [1,4,7,8,6,454,44,5]; var m = Math.min.apply( null,arr ); console.log( m ); //3 类数组转换成真正的数组 var t = {}; t[0] = 1; t[1] = true; t[2] = "laoma"; t.length = 3; console.log( t ); //var m = [ 1,2,3 ]; //m.slice();//如果设么都不穿,默认从0索引开始截取到数组最后 //第一个参数是 截取开始的位置 startIndex //第二个参数是 截取结束的位置+1 endIndex //如果我借用数组的slice方法,然后把this指向到t对象 //那么slice方法就会返回t对象对应的数组 var k = Array.prototype.slice.call( t,0 ); //通过一个slice方法,就可以吧类数组转化成真正的数组 console.log( k ); console.log("=========="); //4 判断代码输出的内容 function Dog(){ console.log( this ); } Dog(); //函数调用模式 var d = new Dog();//构造函数调用模式 Dog.call( null );//借用调用模式 </script>
JavaScript中函数没有重载
示例代码
<script> //arguments模拟函数重载 //创建一个矩形的类型,构造函数接受一个参数,返回一个正方形。 //接收两个参数,返回一个矩形 function React(){ //如果一个参数,返回一个正方形 if( arguments.length == 1 ){ this.width = arguments[0]; this.height = arguments[0]; } //如果两个参数,返回一个矩形 if( arguments.length > 1 ){ this.width = arguments[0]; this.height = arguments[1]; } //由于跟原型上的toString方法重名,那么会覆盖Object原型上的toString方法 this.toString = function(){ return "" + this.width + " height:" + this.height; } } var r1 = new React( 10 ); console.log( r1.toString() ); var r2 = new React( 10,9 ); console.log( r2.toString() ); </script>
函数的递归调用
示例代码:
<script> //求1-100的和 // 一般方法 var sum = 0, n = 100; for( var i = 0;i <= n;i++ ){ sum += i; } console.log( sum ); // 递归的方法 function sumNum( num ){ if( num == 0 ){ //递归 一定要有个结束 自己调用自己 的出口 return num; }else{ //实现自己调用自己 return num + sumNum( num-1 ); } } console.log( sumNum(100) );
示例代码:函数递归调用案例
<script> //函数递归案例 var f = function(){ console.log( "sss" ); }; f();//调用函数表达式执行 //函数命名表达式,fun相当于在函数内部添加一变量fun,fun指向函数自身 //fun === arguments.callee //fun 的作用域在函数内部,不会影响函数外面的声明的参数或者函数 var m = function fun(){ console.log( "fun" ); }; m(); //求num的阶乘,用递归实现 function factorial( num ){ //定义一个出口 if( num == 0 ){ return 1; } return num * factorial( num - 1 ); } console.log( factorial(4) ); //求斐波那切数列 //f(0) = 0 f(1) = 1 //f(n) = f(n-1) + f(n-2) function fibonacii(n){ if( n == 0 ){ return 0; } if( n == 1 ){ return 1; } return fibonacii( n-1 )+ f( n-2 ); } console.log( fibonacii(10) ); </script>
函数式编程
示例代码:
<script> //函数式编程 //复习 Array.prototype.sort可以对数组中的元素进行排序 //排序的算法是根据字符串比较大小的算法进行排序 //不适合于数字的排序 var t = [ 48,2,33,4,55,9,8 ]; console.log( t ); console.log( t.sort() ); //按照数值的大小进行排序 t.sort(function( a,b ){//函数式编程 一个函数作为另一个函数的输入 //如果 a > b return 大于0 的值 //如果 a === b return 0 //如果 a < b return 小于0的值 return a - b; }); console.log( t ); </script>
示例代码
<script> //map方法 var t = [ 1,3,9,10,20 ]; for( var i in t ){ console.log( t[i] ); } //对数组中的每个元素都进行*2,打印结果数组 //map方法:返回一个新数组,每个项处理完成后的结果组成的新数组 //对原数组没哟印象 var m = t.map(function( item,index,array ){ //item: 就是当前的选项 //index:当前选项的索引 //array:当前数组 //return:返回每个项处理的结果 return item * 2; }); console.log( t ); console.log( m ); </script>
示例代码
<script> //数组的forEach方法 var m = [ "22",true,1,99,98 ]; //打印m中的每个元素 m.forEach( function( item,index ){ //item是遍历的每个项 //index项对应数组的索引 console.log( "index:" + index + " " + item ); } ); // console.log( m ); </script>
函数的属性和方法
示例代码:
<script> //函数自有属性 function fun1( a,b ){ console.log( a + b ); } console.log( fun1.length );//函数都有默认的length属性 = 形参的个数 //fun1是函数也是一个独享 就可以当做对象使用 fun1.age = 19; console.log( fun1 ); </script>
1.3垃圾回收
示例代码
<script> // JavaScript 内存管理 var m = 0,n = 19; var t = add( m,n ); //把 a b c 标记为进入环境 console.log( t );//a b c标记为离开环境 等待垃圾回收 function add( a,b ){ a++; var c = a + b; return c; } </script>
垃圾回收的应用
示例代码
<script> //垃圾回收处理 //1 数组清零 var a = [ 1,9,20,333,4,43 ]; a.forEach(function( item ){ console.log( item ); }); //数组清零,让gc尽快的回收a数组的内存的空间 //a = null; //首先 把a转化成null的类型 //a = []; //确实让a变量成一个空数组,但是在堆上重新申请了一个空数组对象 a.length = 0;//可以直接让数字清空,而且数组类型不变 //2 对象复用 var t = {}; //每次循环都会创建一个新的对象 for( var i = 0; i < 10;i++ ){ //var t = {};//每次循环都会创建一个新对象 t.age = 19; t.name = "123"; t.index = i; console.log( t ); } t = null;//对象如果已经不用了,那就立即设置为null:等垃圾回收 //3 在循环中最好也别使用函数表达式 // for( var k = 0;k < 10;k++ ){ // var t1 = function( a ){ //创建了10册函数对象 // console.log( a ); // }; // t1( k ); // } function t1( a ){ console.log( a ); } for( var k = 0;k < 10;k++ ){ t1( k ); } t1 = null; </script>
1.4 原型链和闭包
原型链
构造函数的原型对象
闭包
闭包的应用
示例代码:
<script> //定义了一个 外层的函数 function foo( x ){ var tmp = 3;//定义了一个 局部的变量tmp return function( y ){ console.log( x + y + (++tmp) );//这个函数可以访问的变量 y tmp x bar }; } //调用foo方法执行,并把返回的函数给了 bar 变量 var bar = foo( 2 ); bar( 10 ); bar( 20 ); function d(){ var a = 10; console.log( a ); } d(); </script>
闭包的应用
匿名自执行函数模拟块级作用域示例
<script> // var t = function( a ){ // console.log( a ); // }; // t(9); //尽量少的定义全局变量,尽量少污染全局变量 //匿名自执行函数 ;(function( a ){ console.log( a ); })(9); // ;(function(a){ // console.log( a ); // }(8)); </script>
循环注册dom时间中index示例代码
<body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> //循环注册dom事件典型错误 //从文档结构中获取所有的li标签 var lis = document.querySelectorAll( "li" ); console.log( lis ); //----循环注册事件的index的典型错误------ for( var i = 0;i < lis.length; i++ ){ lis[i].onclick = function( e ){//事情的方法执行是:当事件触发的时候执行 //变量i是福函数里面的变量 console.log( i );//把当前的索引i的值打印出来 }; } // i == 5 //面试题目 //----改正循环注册事件的index的典型错误------ for( var i = 0;i < lis.length; i++ ){ ;(function(a){ lis[a].onclick = function( e ){ console.log( a ); } })(i);//传参数:是做的值的复制,做的副本 } </script> </body>
setTimeOut中的闭包应用
<script> //setTimeOut的闭包应用 //经过多少好秒后,执行回调函数 //bom的一个方法,接受两个参数:第一个参数是回调函数 //第二个参数是经过的毫秒数 setTimeout (function () { //当1000毫秒后,执行当前的函数体 console.log("sss"); }, 1000); for (var i = 0; i < 10; i++) { setTimeout(function () { //注册1秒后的代码执行很快 console.log(i); //函数体在1秒后才执行 }, 1000); } //用匿名自执行函数解决 for (var i = 0; i < 10; i++) { ; (function (a) { setTimeout(function () { //注册1秒后的代码执行很快 console.log(a); //函数体在1秒后才执行 }, 1000); })(i); } </script>
闭包的缺点
1.5面向对象
面向对象的概念
对象属性和行为复用
工厂方式创建对象
示例代码
<script> //E31-工厂模式创建对象.html var a = {};//不能重复利用设置公共属性的代码 //工厂模式 //我需要建立10个猫对象 每个对象都有年龄 星星的属性 包括run方法 function createCat( age,name ){ var o = new Object(); o.age = age; o.name = age; o.run = function(){ console.log( o.name + " run...." ); }; return 0; } var c = createCat( 19,"dddgg" ); //优点 //可以进行批量的创建 都有公共默认值和属性的对象 //缺点 //1 对象的方法不能跟其他对象共享 多占内存 //2 不能识别对象的原型及构造函数 // c instanceof createCat //false </script>
示例图
构造函数创建对象
示例代码
<script> //E32-构造函数创建对象的模式.html function Cat( age,name ){ this.age = age; this.name = name; this.run = function(){ console.log( this.name + " running..." ); }; } //当使用new来调用构造函数的时候 //1 创建一个空对象 //2 把空对象赋值给this //3 执行构造函数里面代码,并给this的属性赋值初始化 //4 把新创建的对象返回(如果有返回值,返回值是简单类型会直接忽略,还返回this) // 如果直接返回引用了类型,直接返回引用对象 //通过构造函数创建一个对象 var c1 = new Cat( 19,"dd" ); c1.age = 20; //修改对象的属性值 c1.run(); var c2 = new Cat( 22,"ss" ); //优点 //1 创建对象的时候默默人初始化一些属性 //2 可以进行使用instance追溯对象的原型及构造函数 // c1 instanceof Cat;//true // c1.constructor === Cat // 缺点 // 对象的方法不能进行重用,每个对象里面都要存储一份方法对象,浪费内存 </script>
原型构建对象
示例代码
<script> function Cat(){ this.age = 19; //如果需要共享的方法和属性,一般放到原型中定义 // this.run = function(){ // console.log( "run" ); // }; } //原型中定义属性和方法 Cat.prototype.run = function(){ console.log( this.name,this.age ); }; //私有的属性希望每个对象私自拥有 Cat.prototype.name = "black cat!"; //所有的新对象都共享这个属性 var c1 = new Cat(); //c1 有自己的run方法 var c2 = new Cat(); //c2 有自己的fun方法 console.log( c1.name ); //black cat! console.log( c2.name ); //black cat! c1.run(); console.log( c1.run === c2.run ); //true c1.name = "good cat"; //对象的属性分为读取和设置两种模式 //如果是读取:自己没有这个属性,那么去原型上找,直到找到为止,如果找不到,返回undefined //如果是吸入:那么自己没有这个属性,那么直接添加一个自己属性 console.log( c1.name,c2.name ); </script>
示意图
组合构造函数模式与原型模式构建对象
示例代码:
<script> //组合创建模式 function Cat( age,name ){ //一般构造函数的首字母大写,我们也成为Cat类 this.age = age; //每个对象都有自己私有的属性值的属性,放到构造函数中 this.name = name; } //一把类型的方法,都放在原型上,让所有的对象都共享方法的内存 Cat.prototype.run = function(){ console.log( this.nam + " running.." ); } var c1 = new Cat(10,"jimi"); var c2 = new Cat(19,"kimi"); console.log( c1.run === c2.run ); c1.age = 20; </script>
示意图
稳妥构造函数模式
示例代码:
<script> //稳妥构造函数模式 function Cat(){ var o = {}; o.age = 19; o.name = "laoma"; o.run = function(){ console.log( o.name + " running...." ); }; return o; //如果是构造函数执行模式,如果返回的是一个引用类型,就把引用类型返回 //如果是简单类型,那么就返回this } // 稳妥构造函数模式,要实现使用new构造一个对象和不使用new构造一个对象效果一样 var c1 = new Cat(); //构造函数调用模式 var c2 = new Cat(); //函数调用模式 //缺点 不能溯源 原型 、构造函数 对象的方法内存不能共享 浪费内存 </script>
对象的继承
示例代码
<script> //原型继承模式 //动物基类 function Animal( age,name ){ this.age = age; this.name = name; } //在动物基类的原型上添加方法run Animal.prototype.run = function(){ console.log( this.name + " running..." ); } function Cat( age,name ){ this.age = age; this.name = name; } //原型的继承方式 //Cat.prototype.constructor === Cat Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; //因为上面的代码把Cat的prototype指向了Animal // console.log( Cat.prototype.constructor ); var c = new Cat( 19,"sss" ); //希望 cat 继承animal的属性和方法 c.run(); //从animal原型上继承的方法 //问题: //1 子类构造函数的参数,没法传递给父类的构造函数 //2 子类的原型的constructor 会被改变 须要自己改回来 </script>
示例代码
<script> //原型继承模式 //动物基类 function Animal( age,name ){ this.age = age; this.name = name; } //在动物基类的原型上添加方法run Animal.prototype.run = function(){ console.log( this.name + " running..." ); } function Cat( age,name ){ this.age = age; this.name = name; } //原型的继承方式 //Cat.prototype.constructor === Cat Cat.prototype = new Animal(); //父类的构造函数:执行第一次 Cat.prototype.constructor = Cat; //因为上面的代码把Cat的prototype指向了Animal // console.log( Cat.prototype.constructor ); var c = new Cat( 19,"sss" ); //希望 cat 继承animal的属性和方法 c.run(); //从animal原型上继承的方法 //问题: //1 子类构造函数的参数,没法传递给父类的构造函数 //2 子类的原型的constructor 会被改变 须要自己改回来 //3 如果父类里有引用类型的属性,那么所有的子类会共享这个引用类型 </script>
组合借用构造函数模式与原型继承模式
示例代码
<script> //组合继承模式 //组合的原型继承和借用构造函数继承 //父类 function Animal( age,name ){ this.age = age; this.name = name; this.food = ["water","fruit","fish"]; } //在父类的原型上创建一个run方法 Animal.prototype.run = function(){ console.log( this.name + " running ..." ); } //定义子类 function Cat( age,name ){ // Animal( age,name ); //this ===window 函数执行模式 this === window //this == c //第一次执行父类的构造函数 Animal.call( this,age,name ); //借用父类的构造函数,给子类创建实例属性 } //第二次执行父类的构造函数 Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var c = new Cat( 19,"bosicat" ); //组合原型继承模式,给子类继承原型的方法和属性 </script>
原型式继承
示例代码
<script> //原型式继承 //o就是要借用的对象 function object( o ){ function F(){} F.prototype = o; //让空函数的原型指向 o 对象 return new F(); //创建一个F实例,f的内部原型指向 o 对象 } var m = { age:19, name:"laoma", friends:[ "laoma1","laoma2" ] }; var m1 = object( m ); console.log( m1.friends ); m1.age = 20; //优点 不需要使用new构造函数就可以直接构造另外其他对象 //缺点 所有构造出来的实例会共享 原型对象上的引用类型的属性 </script>
寄生继承模式
示例代码
<script> //寄生继承模式 //原型式继承的方法 传一个对象o //内部新构造一个对象 新对象的原型指向o function object( o ){ function F() {} F.prototype = o; return new F(); } var p = { age:19, name:"laoma" } //寄生继承方式:其实就是传一个对象到一个方法(工厂方法) //根据传来的对象构造一个新对象 并对新对象进行扩展增强 function createPerson( p ){ var o = object( p ); //用p对象构造一个新对象o o.say = function(){ //对新构造出来的对象o进行扩展 console.log( "hhh" ); }; return o; } </script>
寄生组合继承模式
示例代码
<script> // 寄生组合继承模式 // 组合了:寄生继承模式和借用构造函数继承模式 //父类 function Animal( age,name ){ this.age = age; this.name =name; this.foods = [ "水果","肉" ]; } //父类原型上的方法:通过寄生继承方式进行继承 Animal.prototype.run = function(){ console.log( this.name + " runniing...." ); }; function Cat( age,name ){ //使用借用构造函数继承模式来构建对象的实例属性 Animal.call( this,age,name ); } //Cat.prototype = new Animal(); //多执行了一次父类的构造函数 //寄生继承的方法 Cat.prototype = inheritFrom( Animal.prototype ); var c1 = new Cat( 19,"laoma" ); var c2 = new Cat( 29,"xiaoma" ); c1.run(); c2.run(); //寄生继承模式 function inheritFrom( o ){ var t = object( o ); t.constructor = Cat; //把Cat原型的构造函数指回Cat构造函数 return t; } //原型式继承的方法 function object( o ){ function F(){} F.prototype = o; return new F(); } </script>
私有变量
示例代码
<script> //第一种模拟私有变量的方式 function Person() { var age = 0;//私有变量只能通过getAge和setAge来操作age变量 this.getAge = function () { return age; }; this.setAge = function (a) { age = a; }; } var p = new Person(); p.setAge(90); //访问p的年龄 console.log(p.getAge()); //第二种模拟私有变量 function Per() { var age = 0; return { getAge: function () { return age; }, setAge: function (num) { age = num; } } } var p1 = Per(); //创建一个Per对象 p1.setAge(20); console.log(p1.getAge()); </script>
1.6模块化演变
示例代码
<script> // 问题 function demo(){ var a = b = c = 9; //a 享受了var关键字,b和 c不享受 //如果变量没有声明直接拿过来用,那么就相当于直接声明了全局变量 } demo(); // console.log( a ); //未声明 报错 console.log( b ); //9 console.log( c ); //9 // a.js a开发的 var m = 0; console.log( m ); // b.js b开发的 var m = "sss"; console.log( m ); //团队合作噩梦 变量冲突 //尝试解决命名冲突的问题 //第一个尝试:命名空间 // a.js a开发的 var Shop = {}; //顶层命名空间 Shop.User = {}; //电商的用户模块 Shop.User.UserList = {}; //用户列表页面模块 Shop.User.UserList.length = 19; //用户一共有19个 // b.js b开发的 Shop.User.UserDetial = {}; Shop.User.UserDetial.length = 20; console.log( Shop.User.UserDetial.length ); console.log( Shop.User.UserList.length ); //=> 给单个文件里面定义的局部变量 编程 局部作用域里面的变量 //第二个尝试 // a.js ;(function(){ var a = 9; })(); // b.js ;(function(){ var a = "sss"; })(); //局部作用域和命名空间的用法减少了变量冲突的可能性 //第三种尝试:希望能把自己积累的的很多工具封装一个整体的框架 //btn form animate // laoma.js ;( function(w){ //判断老马框架是否存在,如果不存在就初始化创建已给啊 if( !w.laoma ){ w.laoma = {}; } // var laoma = {}; w.laoma.Btn = { getVal:function(){ console.log( "getval" ); }, setVal:function( str ){ console.log( "setval" ); } }; // window.laoma = laoma; //把老马对象传递给window全局变量 } )(window||{}); // laoma.animate.js // 动画组件 ;(function(w){ if( !w.laoma ){ w.laoma = {}; } w.laoma.animate = {}; })(window||{}); // laoma.form.js ;(function(w){ if( !w.laoma ){ w.laoma = {}; } w.laoma.form = {}; })(window||{}); </script>
1.7 正则表达式
正则表达式学习工具:https://c.runoob.com/front-end/854
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
元字符
限定符
括号相关
贪婪模式
常见验证案例
JavaScript中使用正则表达式
示例代码
<script> // 创建一个正则表达式对象 d+ var exp1 = new RegExp( "\d+","gi" ); // 创建表达式对象可以用 //g 创建 var exp2 = /d+/g; // console.dier( exp1 ); // console.dier( exp2 ); //正则对象的test方法,接受一个字符串,然后进行匹配,如果匹配上了,返回true,否则false console.log( exp1.test("24234jljl") ); console.log( exp2.test("jljl") ); </script>
正则对象的属性和方法
示例代码
<script> // E44-正则的exec方法.html var str = "12,34,56"; var exp = /d{2}/g; // console.dir( exp.exec(str) ); var temp; //exec方法:如果没有匹配项,那么就会返回null //如果有匹配的返回一个数组 //0 p匹配的字符串 //index 匹配开始的索引 //input 要匹配的字符串 str 原始的字符串 while( (temp = exp.exec(str)) != null ){ console.log( exp.lastIndex ); //当前匹配完了之后,下一次匹配开始的位置 console.log( temp[0] ); } //如果有分组 var temp1; var str2 = "12abc,34,fde,45asf"; var exp2 = /d{2}(w)(w+)/g; while( (temp1 = exp2.exec(str2)) != null ){ console.log( exp2.lastIndex ); //当前匹配完了之后,下一次匹配开始的位置 console.log( temp1[0] ); } </script>
字符串中支持正则的方法
正则案例
示例代码
<script> //将字符串 1392945632000,mss,Date(1392945632000) 其中绿色部分的数字转化成日期对象 var str = "1392945632000,mss,Date(1392945632000)"; var t = eval( str.replace(/.*(Date(d+)).*/g,"new $1" ) ); console.log( t.toString() ); </script>
1.7 JavaScript的异常处理
错误信息对象Error
示例代码
<script> try{ console.log( 1 ); var t = new jflsj();//报错 console.log( 2 ); }catch(e){ console.log( e.message ); }finally{ console.log( "finally" ); } alert( "232" ); //throw try{ add( "w","4" ); }catch( e ){ console.dir( e ); }finally{ console.log("结束"); } function add( a,b ){ if( typeof(a) != "number" ){ throw "传入参数不是整数类型"; // throw new Error( "传入参数不是整数类型" ); //ff chrome浏览器 // IE浏览器 第一个参数是错误行号 第二个参数是 错误信息 // throw new Error(26,'传入参数不是整数类型'); //ie edge // var e = new Error; // e.message = "传入参数不是整数类型"; // e.name = "parame type error"; // throw e; // 只要遇到throw 程序就停止了 // 代码跳转到catch语句 如果没有遇到catch语句当前js程序会结束 } return a + b; } </script>
未完待续