作者声明:本博客中所写的文章,都是博主自学过程的笔记,参考了很多的学习资料,学习资料和笔记会注明出处,所有的内容都以交流学习为主。有不正确的地方,欢迎批评指正
本节课学习视频来源: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>
未完待续