笔记 - JavaScript - 超哥视频
第一天:
【01、基础】 ● 什么是JavaScript:是网景(Netscape)公司开发的一种基于客户端(浏览器)、面向(基于)对象、事件驱动型的脚本语言。 ● 在HTML中使用JS有3种方法: 1.行内调用,例如<a href="javascript: void(0);">; 2.使用script标签嵌入代码; 3.使用script标签引用外部的独立.js文件;引入文件的script标签内部填写的代码不会被使用。 ● 在JS中,代码区分大小写;语句末尾需要加分号;script标签中不能嵌套HTML标签……等。 ● 变量是用来临时存储数值的容器,变量存储的数值是可以变化的。 ● 变量需要先声明才能使用,使用var声明变量;不使用var变量属于全局变量。 ● 在JS中,有以下六种数据类型:Undefined(未定义)、Null(空值)、String(字符串)、Number(数字)、Boolean(布尔型)、Object(对象)。 ● 在JS中,算数运算符包括:+、-、*、/、%、++、--。 ● 后++是先赋值再自加,前++是先自加再赋值。例如: var i = 10; var j = i++;//先赋值再自加 var k = ++i;//先自加再赋值 alert(j);//10 alert(k);//12 ● 在JS中的比较运算符包括:>、<、>=、<=、!=、==、!==、===。 ● 等于==和全等于===的区别为:前者只判断值是否相等,后者判断值和数据类型是否都相等。 ● 在JS中,逻辑运算符包括:&&、||、!。 ● 在JS中,赋值运算符包括:=、+=、-=、*=、/=、%=;作用是将运算符左边的值与右边的值进行计算后,将结果赋值给左边。 ● 在JS中,字符串运算符(字符串连接符)包括:+、+=。 ● 在JS中的流程结构包括: 1.顺序结构,JS代码是一行接着一行执行; 2.分支结构,如:if、else、else if、switch; 3.循环结构,如:for、while、do...while、for...in。 ● while(true)是无限循环,可以在循环中加入满足设定的条件break跳出循环。
【02、函数】 ● 函数的功能: 1.代码复用; 2.模块化编程; ● 函数需要先定义才能调用,函数定义有三个部分:函数名,参数列表以及函数体。例如: function fnName(arg1, arg2, ...aegN) { function code; returen code;//可忽略,返回值类型也无需定义; } fnName(arg1, arg2, ...argN);//调用函数; ● 普通函数不经过调用,不会自动执行。 ● 在函数执行过程中,形参值的改变不会影响实参,因为形参与实参之间是按值传递,并非按地址传递。 ● 在JS中,对象类型默认就是按地址传递。例如: function fn(obj) { obj.name = 'b'; } var e1 = {}; e1.name = 'a'; fn(e1); alert(e1.name);//弹出结果为b; //以上例子中,实参传值为对象类型,传值时采用的是按地址方式,故函数内部将地址指向的内存空间里的值改变了; ● 由于函数使用场景一般由调用者决定,所以最好使用返回值将结果传递出去;函数遇到renturn关键字会立即返回,后面代码不执行。 ● 变量可以保存数据,也可以保存地址;变量名存在栈区,变量数据存在堆区,因为堆比栈大,栈比堆快;函数存在代码段区,使用栈区的名称指向。 function fn() { return 'j'; } var a = fn;//不带括号代表将fn函数的首地址赋值给i变量,调用i()等同于调用fn(); var a = fn();//带括号代表将fn函数执行后的结果赋值给i变量,最后i = 'j'; ● 函数在创建时便会返回自己的首地址,全局变量的首地址成为window对象的一个属性,当然也可以把首地址赋值给自定义变量; ● 自调用匿名函数可以避免函数名重复而导致函数被重写,并且只会在运行时执行一次,一般用来初始化工作。方式如下: (function (arg) { alert(arg); })('abc');//先将匿名函数用括号括起来看做一个整体,然后用这个整体返回的函数首地址进行调用以及传参; ● 不使用var关键字声明的变量在任何位置都是全局变量,使用var声明的变量在函数内部是局部变量;局部变量只在局部作用域起作用。 ● 在JS中,若一个变量没有通过var声明,会自动到上一层作用域查找变量的声明语句,若找到,便使用找到的语句,若没找到,便继续向上查找直到全局作用域为止, 若在全局作用域仍未找到,便自动在全局作用域声明;这就是JS中的作用域链。 var i = 10; function fn1() { i = 100; function fn2() { i = 1000; function fn3() { i = 10000; } fn3(); } fn2(); } fn1();//函数不调用是不会执行的,先看调用与否,再去判断函数内的值是否改变; alert(i);//弹出结果为10000; ● 局部作用域访问全局作用域变量使用作用域链,全局作用域访问局部作用域变量可以使用函数闭包进行模拟。 ● 在一个函数内部,可以使用arguments属性,它表示函数的参数列表,它是以数组形式体现的。 ● 定义函数时若没有定义形参,但调用此函数时,有实参的情况下,实参会自动存放到arguments这个数组属性中。例如: function fn() { for(var i = 0; i<arguments.length; i++){ alert(arguments[i]); } } fn('a', 'b', 'c');//分别弹出a、b、c; fn(1, 2, 3, 4, 5);//分别弹出1、2、3、4、5;
【03、词法分析】 ● 在JS中,包含在script标签中的代码是分段执行的,当有多个script代码段时,计算机会按顺序对每个代码段进行读入、编译和执行。 ● 在JS中,“编译”是指计算机将JS代码自动进行变量声明、函数声明、语法检查、语义检查、代码优化、分析并得到代码树等步骤。 ● 由于在执行代码之前,JS代码已经完成编译,意味着所有函数已经声明,故将调用指定函数代码写在指定函数之前,也可以成功调用指定函数。 ● 变量在代码编译后,只完成了声明,代码执行才会进行赋值。例如: alert(a);//编译错误(a is not defined),并且程序无法继续执行,因为编译时并没有声明变量a; alert(i);//弹出结果为undefined,此时编译完成,变量i已经声明但还未执行赋值; var i = 10;//变量i进行了赋值; alert(i);//弹出结果为10; ● 在JS中的错误类型包括编译错误和运行错误,编译错误指编译时发生的错误,例如语法错误;运行错误指在执行阶段发生的错误。 ● 在当前运行错误的代码之前的代码都可以被之执行;而只要有一处编译错误的代码段,包含此错误代码的整个script代码段由于无法通过编译阶段而不能执行。
第二天:
【01、数组】 ● 数组就是一组数据的集合,其表现形式就是内存中一段连续的内存地址,数组名称其实是连续内存地址的首地址;在JS中,数组不是数据类型,而是一个对象。 ● 在JS中,数组定义时无需指定数据类型和数组长度,并且一个数组中可以保存不同类型的数据。 ● 在JS中,创建数组的方式有四种。例如: var arr = [data1, data2, ..., dataX];//隐式创建,常用的方式; var arr = new Array(data1, data2, ..., dataX);//创建新数组对象并实例化; var arr = new Array(length);//创建并指定长度,不推荐使用; var arr = new Array();//创建一个数组,不指定长度,可以使用var arr = []来代替; ● 在JS中,使用for...in语句可实现遍历对象的所有属性,同理也可遍历数组存储的所有元素;数组中有几个元素,语句就循环几次,每次循环会将数组元素的下标存入变量i中。 ● 在JS中,创建好数组后,可以创建带有文本下标的数组元素,但是这种数组元素不计入数组长度,因为这种元素是以属性形式添加到数组对象中的。例如: var arr = [1, 2, 3]; arr['first'] = 'abc';//这种方式和arr.first = 'abc'的结果是一毛一样的; arr['second'] = 'def'; alert(arr.length);//弹出结果为3; alert(arr.first);//弹出结果为abc; ● 要遍历带有文本下标的数组对象,无法依赖length属性,必须通过for...in语句进行遍历。 var oNewOption = new Option(text, value);//Option属于DOM中的内置对象,指页面中的option元素,实例化时传参为文本和值; selectElem.options;//返回select元素下的option元素集合; selectElem.options.add(optionObj);//使用options对象的add方法添加一个Option对象实例; selectElem.selectedIndex;//返回当前选中的option下标; ● ※问题:options.length = 0;设置options的length属性为什么Webstorm会报错?提示(Attempt to assign to const variable.),有病……
【02、事件编程】 ● 常用事件: onload;//页面加载完成事件句柄,一般用于body元素; onunload;//页面关闭完成事件句柄,一般用于body元素; onblur;//失去焦点事件句柄,表单验证时用到最多; onfocus;//获得焦点事件句柄; onclick;//鼠标按键单击事件句柄; ondbclick;//鼠标按键双击事件句柄; onmouseover;//鼠标移入事件句柄; onmouseout;//鼠标移出事件句柄; onmousedown;//鼠标按键按下事件句柄; onmouseup;//鼠标按键松开事件句柄; onmousemove;//鼠标移动事件句柄; onchange;//区域内容改变事件句柄,一般用于select、input及textarea元素; onkeydown;//键盘按键按下事件句柄; onkeypress;//键盘键入事件句柄,无内容输入的功能性按键不会触发此事件,例如:ESC, Shift, alt, Crtl以及方向键等等; onkeyup;//键盘按键抬起事件句柄,三种键盘事件触发顺序依次为:down、press、up; onsubmit;//表单提交事件句柄,一般用于form元素; onreset;//表单重置事件句柄;一般用于form元素; ● 在JS中,事件绑定方式有行内绑定、动态绑定和事件监听; ● 行内绑定事件和动态绑定事件最大的区别是:行内绑定事件触发后执行的函数都属于全局函数,会产生this对象的指向window对象的问题。例如: <script type="text/javascript"> function fnRed() {//下面div1行内绑定事件执行此函数,这里this实际指向的是window对象,所以点击后,它的文字颜色不会变红; this.style.color = 'red'; } window.onload = function () { var oDiv = document.getElementById('div2'); oDiv.onclick = fnBlue;//oDIv.onclick也被赋值fnBlue这个函数的首地址,所以函数内部的this指的就是oDiv对象,点击后,文字变蓝; //oDiv.onclick();再简化就可以看出来,this指向的是oDiv,非常明显了; function fnBlue() {//下面div2动态绑定事件执行此函数; this.style.color = 'blue'; } } </script> <div id="div1" onclick="fnRed();">点击文字变红</div> <div id="div2">点击文字变蓝</div> ● 关于this指向问题,只要牢记,属于谁,指向谁; ● 在JS中,如果为同一个对象添加多个相同事件句柄来处理程序,那么只有最后一个会生效,前面的都会被覆盖;很好理解,事件句柄本身可看作对象的属性,为相同属性多次赋值必然会覆盖。 ● 如果需要为同一对象的相同事件添加不同的事件处理程序,可以使用事件监听。语法: obj.attachEvent(type, listener);//为指定对象绑定需要的事件句柄来处理程序;type是事件句柄,类型string;listener是要调用函数的名称;只支持IE10及以下版本的IE浏览器。 obj.addEventListener(type, callback, capture); //type是事件名,string类型,callback是要调用函数的名称,capture为可选,是事件模型,boolean类型,传ture为捕捉模型,默认false,代表冒泡模型;支持IE9及以上的IE和其他浏览器。 ● 以上两种事件监听的触发顺序不同:attachEvent是先绑定的后触发,addEventListener是先绑定的先触发。 ● 事件模型分为冒泡模型和捕获模型两种;IE值支持冒泡模型。 ● 大多数情况下,需要禁用事件冒泡。语法: eventObj.cancelBubble = true;//低版本FF、chrome浏览器不支持; eventObj.stopPropagation();//IE8及以下版本IE浏览器不支持; ● 在JS中,默认行为是指某些HTML元素或浏览器有自己的行为,比如:点击鼠标右键弹出环境菜单、点击提交按钮会提交表单、超链接的跳转等等。 ● 有时某些默认行为需要禁止,如表单提交时,验证不通过便阻断提交,一般可以使用添加return false语句的方法。另一种是: eventObj.returnValue = false;//FF、IE11浏览器不支持; eventObj.preventDefault();//IE8及以下版本IE浏览器不支持; ● 事件对象是事件触发时系统自动产生的对象,此对象包含了事件触发时的所有信息;如鼠标移动时,鼠标所在的X坐标和Y坐标都会保存在事件对象中。 ● IE8及以下版本IE浏览器获取事件对象使用window的event对象,其他浏览器使用在事件调用函数的第一个参数获取。 eventObj.keyCode;//获取触发键盘按键事件的对应值,各种不同键盘返回值都相同。
【03、BOM模型】 ● 当用浏览器打开一个网页时,JS会自动创建对象,首先创建浏览器对象window,再为window创建其子对象,例如:document、location、navigator等,最后形成一个树状模型,称为DOM模型。 ● 常用window对象的方法: alert(code);//警告消息框; confirm(code);//确认消息框,点击确认返回true,否则返回false; prompt(code, defStr);//输入消息框,返回输入的数据,没有输入返回null或默认数据; open(url, name, features);//打开一个指定地址的窗口或返回一个设置了名称的窗口; close();//关闭窗口; print();//打印窗口; moveBy(x, y);//相对原先的位置移动窗口; moveTo(x, y);//移动窗口到指定位置; resizeBy(x, y);//相对改变窗口尺寸; resizeTo(x, y);//绝对改变窗口尺寸; scrollBy(x, y);//相对滚动; scrollTo(x, y);//绝对滚动; setTimeout(code, ms);//延迟定时器; setInterval(code, ms);//循环定时器; clearTimeout(timeoutObj);//清除定时器对象,关闭延迟定时器; clearInterval(IntervalObj);//清除定时器对象,关闭循环定时器; ● 可以利用自调函数来使setTimeout循环执行。 ● 延迟定时器无阻断性,执行完其代码后JS不会等待结果而会继续向下执行;当程序执行延迟定时器方法时,会向系统抛出一个定时器对象,到达指定时间后,定时器对象自动执行指定语句,而后消失; ● 常用navigator对象(浏览器环境对象)的属性: appCodeName;//浏览器内部代码; appName;//浏览器名称; appVersion;//浏览器版本号; platform;//操作系统信息; onLine;//在线状态; cookieEnabled;//cookie支持状态; ● 常用location对象(地址对象)的属性: host;//主机名; port;//端口号; href;//完整的URL信息; pathname;//路径地址; protocol;//协议; search;//查询字符串; assign(url);//页面跳转方法; ● 常用screen对象(屏幕信息对象)的属性: availHeight;//屏幕可用高度,从屏幕上下一边到任务栏边际; availWidth;//屏幕可用宽度,从屏幕左右一侧到任务栏边际; height;//屏幕高度; width;//屏幕宽度; ● 常用document对象(文档对象)的属性; linkColor;//超链接颜色; alinkColor;//作用中的超链接颜色; vlinkColor;//作用后的超链接颜色; bgColor;//背景颜色; fgColor;//前景颜色(字体颜色); title;//文档标题; ● 常用的DOM节点选择方法: getElementById(id);//通过id属性获取节点,返回一个节点; getElementsByTagName(tagName);//通过标签名获取节点,返回符合条件的节点集合; getElementsByName(name);//通过name属性获取节点,返回符合条件的节点集合; getElementsByClassName(className);//通过class属性获取节点,返回符合条件的节点集合; ● 数据集合和数组是不同的;集合不一定有数组的所有属性和方法;
第三天:
【01、类与对象】 ● 面向对象的两个基本概念: 1.类:代表某类型事物,是抽象的; 2.对象:代表某个事物,是具体的。 ● 常用系统类: 1.String类: length;//返回字符串长度; indexOf(strArg);//取得参数在字符串中出现的位置,若参数值不存在便返回-1; substr(index1, subLength);//截取从指定下标到指定长度的字符串; substring(index1, index2);//截取从指定起始下标到截止下标之间的字符串,不包含截止下标位置(含前不含后); toLowerCase();//将字符串转换为小写; toUpperCase();//将字符串转换为大写; replace(str1/reg,str2);//将指定的或符合正则的字符串替换为新字符串; 2.Date类: getYear();//返回年份; getFullYear();//返回四位年份; getMouth();//返回月份(0~11); getDate();//返回日期数; getDay();//返回星期数(0~6,星期日为第一天); getHours();//返回小时数; getMinutes();//返回分钟数; getSeconds();//返回秒数; getMilliSeconds();//返回毫秒数; getTime();//返回自1970年1月1日至今的毫秒数; 3.Math类: ceil(num);//返回大于或等于该数的最小整数; floor(num);//返回小于或等于该数的最大整数; min(num1, num2, ...numX);//返回指定数值中的最小数; max(num1, num2, ...numX);//返回指定数值中的最大数; pow(num1, num2);//返回num1的num2次方; random();//返回0~1之间的随机数,不包含1; round(num);//将指定浮点数四舍五入为整数; sqrt(num);//将指定数值开平方根; ● Math类属于静态类,其拥有的方法也都是静态方法,不需要实例化,而直接使用类调用。 ● 在JS中没有类的定义语句,只有function,每个function都可以认为是同名类的构造函数,也称为构造器。 ● 在JS中声明类和声明对象的方法如下: function ClassName() {//声明类 } var obj = new ClassName();//声明对象; ● 在类的实例化时,会开辟相应的内存空间,然后会自动执行构造函数。 ● 在JS中,对象的属性都是动态添加的;为对象添加或调用对象属性有两种方式: obj.attr;//一般采用这种方式; obj['attr'];//证明对象的属性可以是数组,而数组也是一个对象; ● 在JS中,对象的属性可以是任何数据类型,特别要理解,属性也可以是一个对象,而对象可以拥有属性,例如window(对象).document(属性/对象).write()(方法)。 ● 在JS中,一切皆为对象。 obj.constructor;//返回对象的构造器; typeof obj;//返回对象的数据类型; obj instanceof ClassName;//判断对象是否为某个类的实例,返回true则是,反之则反; ● 在JS中,对象在内存中的表现形式为:创建对象后开辟对应的内存空间,对象名存储在栈内存中,在对象名所在内存中还存有一个内存地址,这个地址指向对象属性所在的堆内存中的位置。 ● 在JS中,简单数据类型(值类型)全部都在栈内存中存储,复杂数据类型(引用类型)分别存储于栈内存和堆内存中。 ● 再次强调,对象之间的赋值是按地址传递,例如var obj2 = obj1,是将obj1所在栈内存中存储的堆内存指向地址,赋值给了obj2,二者指向的堆内存地址相同;所以修改其中一个对象 的属性,就会影响另一个对象的对应属性;但如果删除obj2,是不会影响obj1的,因为删除obj2只相当于删除它在栈内存中地址的引用而已。 ● 对象之间的赋值一般分为两种:做为参数传递和做为返回值返回。 ● 如果要知道对象中有多少属性,需要使用for...in循环语句,对象下有多少属性,循环语句便执行多少次,每次执行会将当前对象的属性名赋值给i变量。 ● 在for...in循环语句中获取属性需要注意使用obj[i]来获取对象属性,而不是obj.i,obj.i访问的是obj对象下名称为i的属性。 ● 使用for...in语句不仅可以循环自定义对象,也可以循环遍历系统对象; ● 如果需要删除指定对象的某个属性,可以使用delete关键字。格式:delete obj.attr;delete关键字只能删除自定义对象属性,无法删除系统对象属性。
【02、this指针】 ● 如果想在创建对象的同时,自动拥有某些属性或方法,或者在相同的类下创建的对象需要有相同的属性或方法,都需要使用this指针。 function ClassName(arg1, arg2,...) {//构造函数传参; this.attrName1 = arg1;//将参数赋值给类的属性; this.attrName2 = arg2; ... this.fn = function () {//成员方法; ... }; } var obj1 = new ClassName(); var obj2 = new ClassName();//通过new关键字创建的实例,会在内存中开辟新内存空间存储实例对象; ● 对象的方法就是函数,存储在内存的代码段区域,函数名就是这个函数代码段的首地址,而这个首地址存储在对象的属性所在的堆内存中。 ● 在JS中,函数都是由对象调用并运行的,在函数中的this就指向了调用函数的对象,所以所有的函数都是面向对象调用,普通函数是由window这个顶级对象调用的。 ● 直接在浏览器中运行以下代码,结果说明window对象是Window类的实例: alert(typeof this);//弹出结果为object; alert(this.constructor);//弹出结果为Window类的构造函数; ● ※判断this的指向时,主要看this所在的函数是哪个对象调用的,而不是看this在哪个函数中,this指向的必然是一个调用了函数的对象。
【03、json对象】 ● 对象是由若干属性构成,这些属性没有顺序,简而言之:对象是属性的无序集合;集合是指“名”/“值”对(键值对)的集合;在JS中,可以使用{}表示这种集合, 称为json对象。格式: {key1: value1, key2: value2, ...keyX: valueX}。//key值可以使用""或''括起来,也可以不使用,他们表达的含义是相同的; ● 给json对象添加属性时,属性值为一个函数,代表这个属性属于json对象的成员方法;另外,可以定义json对象数组来存储多个json对象。 ● 通过constructor属性查看一个json对象的构造函数,发现json对象是Object类的实例;Object是所有JS类的父类。 ● 在JS中,可以通过构造函数创建对象,也可以使用json来创建对象;二者的特点是:使用构造函数可以简化属性的定义,适用于当程序需要多个相同属性和方法的对象时; 使用json创建对象省略了构造函数的定义,适用于程序只需要一个特定对象时。
【04、prototype的使用】 ● 功能:prototype可以返回对象类型的原型的引用。格式:ClassName.prototype。 ● 如果要创建多个具有相同属性的类的实例,而这个类没有这个相同的属性时,修改类是不可取的,要使用类的prototype属性来添加需要的属性,添加后,这个类的所有实例都会拥有新添加的属性。 ● 使用prototype和不使用prototype添加属性或方法之间的区别是:使用prototype是为类原型的所有实例添加属性或方法;不使用prototype是指为类原型本身添加静态属性或静态方法。 ● 在JS中,加载类的构造函数同时时,系统会为这个构造函数创建一个原型对象,两者之间独立存在但有相互指向关系;构造函数的prototype属性指向了原型对象, 而原型对象的constructor属性指向了构造函数;当类的实例在使用一个不存在的属性时,会通过自身内部的prototype到构造函数的原型对象下寻找; 所以通过prototype属性可以为原型对象添加新的属性。测试: alert(ClassName.prototype.constructor.prototype.constructor);//弹出结果为ClassName的构造函数; var obj = new ClassName(); alert(obj.constructor);//弹出结果为ClassName的构造函数;这里可以看出,obj并没有定义constructor属性,却可以正确使用,证明了上面的prototype属性的存在和原理; ● 在JS中,所有类的父类都是Object,原型对象也属于Object类的实例;当创建原型对象时JS内部会执行ClassName.prototype = new Object;语句,原型对象除了拥有对应的类的属性和方法外, 还继承了Object所有属性和方法,那么类的实例就可以通过原型对象使用这些属性和方法,这就叫做原型继承。 ● 既然Object也是一个类,那么它也有相应的原型对象; ● 当自定义类的实例中没有找到需要的属性,那么会通过prototype在自定义类的原型对象中找,如果原型对象中没有要找的属性,原型对象会通过自身的prototype在Object类的原型对象中找, 而在Object类的原型对象中仍未找到,便返回未找到的结果,这就叫做原型链。
第四天:
【01、Object类】 ● 在JS中,Object是所有类的基类(或称父类),使用Object类来创建自定义对象时,可以无需定义构造函数;当需要在程序中创建一个只用来存储大量数据的对象时,可以考虑使用Object创建。 obj.hasOwnProperty(attr);//判断对象中是否有指定属性;有则返回true;反之则反。 ● 自定义类的实例中,没定义hasOwnProperty方法就能使用,这是因为所有类,包括系统类和自定义类,都是Obeject类的实例,当所有类加载后,系统创建了这些类的原型对象, 通过原型对象而继承了Object类的属性和方法。 ● 在JS中,可以使用ClassName.attr的方式模拟静态属性或方法。
【02、函数闭包】 ● 所谓闭包,指的是一个拥有许多变量和绑定了这些变量的环境表达式(通常是一个函数),因而这些变量也是该表达式的一部分。//解释的真恶心。 ● 在JS中,全局作用域无法访问局部变量,因为作用域不同,另外,函数执行完毕后,局部变量会被回收。 ● 闭包的作用是:访问局部变量和使局部变量所占的内存不被释放。例如: function fn1() { var i = 10;//声明局部变量i,一般情况下在全局作用域是访问不到它的; function fn2() {//声明局部函数fn2; alert(i++);//由于fn2在fn1的作用域里,fn2可以访问到i变量,并使其++; } return fn2;//将fn2函数首地址作为返回值返回; } var test = fn1();//将fn1函数运行后的返回值赋值给test变量,由于fn1的返回值是fn2的首地址,这行代码的效果等同于test = fn2,但直接这样写是不能实现的; test();//相当于变相地执行fn2函数,达成了在全局作用域访问局部函数和局部变量的效果; //以上代码中,test变量通过fn1的返回值指向了fn2的首地址,fn2此时做为一个局部函数却因为外部作用域变量的指向而不能被回收,而fn2中访问的局部变量i因为相同原因也无法被回收。
【03、面向对象高级】 ● 在JS中,可以使用var关键字在局部作用域声明变量,然后使用类的方法来设置(不添加设置方法就是只读属性)和获取(不添加获取方法就是只写属性)这个变量,借此来模拟类的私有属性。 ● 在JS中,函数内部的this会随着程序运行指向不同的对象,但是通过call方法和apply方法,可以手动指定this的指向。语法: targetFn.call([thisObj[, arg1[, arg2[, argN]]]]);//使用指定的对象调用指定函数;参数都为可选,第一参数后的参数按顺序执行,若不指定第一参数,将由Global对象代替; targetFn.apply([thisObj[, [argArr]]]);//使用指定的对象调用指定函数;两个参数都为可选,第二个参数必须是数组,若不指定第一参数,将由Global对象代替; ● 使用call或apply的方法,系统执行的过程是:先将指定的函数中this指向为参数中传入的第一个参数代表,然后再执行函数。 ● 使用给对象添加方法的方式虽然也能达到使用call或apply的效果,但是这两种方式有本质区别,call和apply只是改变了函数中this的指向,而直接添加的方式会让对象占用更多的内存,不推荐使用。 ● 在JS中,类与类之间的继承实现一般由三种方法模拟: 1.扩展Object方法;语法: Object.prototype.ext = function (parObj){//这里的参数需要传入的是父类的实例,而不是父类的类名; for(var i in parObj){ this[i]=parObj[i]; } } 2.使用call、apply方法;语法: function sonObj(arg...) { parObj.call(this, arg...); } 3.使用原型继承方式,推荐;语法: sonObj.prototype = new parObj();
【04、案例】 ● 在实际开发中,使用面向对象添加DOM元素,可以给类添加一个属性专门存放新建的DOM元素,以便其他类的实例可以访问。
第五天:
【01、正则对象】 ● 正则表达式本身就是一种语言,这在其它语言里是通用的;它描述了一种字符串匹配模式,可以检查一个串中是否含有某种字符串,将匹配的子串做替换或从某个串中取出符合某个条件的子串。 ● 要使用正则表达式,必须先创建正则对象,创建方式有两种:一种是隐式创建,例如:var reg = /^\d$/;另一种是直接实例化RegExp类,例如:var reg = new RegExp(pattern,attr)。 ● 在JS中,使用正则对象主要有两种方法: 1.RegExp类: test(str);//匹配指定的模式是否出现在字符串中; exer(str);//返回匹配模式的字符串; 2.String类: search(patten);//匹配符合匹配模式的字符串出现的位置,没有匹配返回-1; match(patten);//以数组形式返回匹配模式的字符串,没有匹配返回null; replace(patten,'str');//将符合匹配模式的或指定的字符串替换为新字符串; split(patten);//使用匹配模式的字符串作为分隔符对字符串进行分割并返回为一个数组; ● 在正则匹配模式中,使用一对括号括起来的内容是一个子表达式;子表达式匹配到的内容会被系统捕获至系统缓存区中,并按捕获顺序编号;捕获到的内容可以在同一条匹配语句中引用, 使用\n(n为数字)来表示引用缓存区中的第几个子表达式,如果需要在另外的匹配语句中引用,则需要使用$n(n为数字)来表示缓存的子表达式顺序编号,这种方法称为反向引用。 例如:要在abcdabc123abcoooqwe456qweyee字符串中,查找3个数字加前后字母相同的字符串组合,规则为/(\w+)\d{3}\1/gi; ● 使用exec方法时,会将匹配的子表达式和匹配后的结果一并以数组形式返回,数组第一个元素为结果,余下的元素各保存一个子表达式。
【02、正则语法】 ● 正则表达式由普通字符(如a、c、123等)和特殊字符(元字符,如\d、\W、.等)组成。 ● 使用正则表达式先确定要查什么,再考虑从哪里查,最后决定查多少。 regObj.lastIndex;//返回下一次开始匹配的起始字符下标; eval(str);//计算指定字符串,并执行其中的JS代码; ● 正则表达式中的常用元字符: 1.限定符: * //匹配前面的组件零次或多次(任意次),等价于{0,}; + //匹配前面的组件至少一次或多次,等价于{1,}; ? //匹配前面的组件零次或至少一次,等价于{0, 1}; {n} //匹配确定的n次; {n,} //至少匹配n次; {n,m} //至少匹配n次,至多匹配m次; 2.字符匹配符: 用[]括起来的匹配符又称为字符簇: [a-z] //表示a-z任意一个字符; [A-Z] //表示A-Z任意一个字符; [0-9] //表示0-9任意一个数字; [0-9a-z] //表示0-9、a-z任意一个字符; [0-9a-zA-Z] //表示0-9、a-z、A-Z任意一个字符; [abcd] //表示a或b或c或d; [1234] //表示1或2或3或4; [^a-z] //表示排除a-z之间的任意一个字符; [^0-9] //表示排除0-9之间的任意一个字符; [^abcd] //表示排除a、b、c、d; \d //匹配一个数字字符,等价于[0-9]; \D //匹配一个非数字字符,等价于[^0-9]; \w //匹配包括下划线的任何单词字符,等价于[0-9a-zA-Z_]; \W //匹配任何非单词字符,等价于[^\w]; \s //匹配任何空白字符,包括空格、制表符、换行符; \S //匹配任何非空白字符; . //匹配除换行符(\n)之外的任何单个字符,如果想匹配任意字符可使用[.\n]; 3.定位符: ^ //匹配字符串的开始位置; $ //匹配字符串的结束位置; \b //匹配一个单词边界; \B //匹配非单词边界; 4.转移符: \ //匹配特殊字符时,需要加转移符转义; 5.选择匹配符: | //可以匹配多个规则; ● 在正则表达式中,当匹配限定次数有上限和下限时,那么表达式会自动匹配不超过上限的尽可能多的次数,这就是正则表达式中的贪婪匹配原则;如果在表达式后面使用?号,表示非贪婪匹配原则, 表达式则会匹配不低于下限的尽可能少的次数。 ● 正则表达式中的几种特殊用法: 1.正向预查: (?=) //查询符合匹配模式的,且是在等号后的指定字符位置之前的字符串; 2.负向预查: (?!) //查询符合匹配模式的字符串,但排除在等号后的指定字符之前的查询结果; 3.结果不被捕获: (?:) //当一段规则只是为了提高优先级而用括号括起,为了避免被当做子表达式存入缓存,可将规则写入这条语句中的冒号后面;
【03、JS中的异常处理】 ● 在JS中,try...catch语句可以捕获并抛出错误信息,但是它一般被用来解决兼容性问题。语法: try{ var i = 10; fn();//此函数没有定义过,必然出错,错误会被catch语句捕获,错误的相关信息会被保存到对象e中; } catch (e) {//e参数是JS中错误类的实例; alert(e.message);//使用message方法弹出错误信息; } ● 使用try...catch语句可以解决的兼容性问题很多,例如函数的事件对象、以及下面例子中的ajax对象等等: try { var xhr = new XMLHttpRequest(); } catch (e) { var xhr = new ActiveXObject('Microsoft.XMLHTTP'); }
完成。