javacript高级程序设计学习笔记
布尔操作符
!false //true
!"blue" //false
!0 //true
!NaN //true
!" " //true
!12345 //false
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作不一定返回布尔值;此时,它遵循以下规则:
1.如果第一个操作数是对象,则返回第二个操作数;
2.如果第二个操作数是对象,则只有第一个操作数的求值结果为true的情况下才会返回该对象
3.如果两个操作数都是对象,则返回第二个操作数;
4.如果有一个操作数是null,则返回null;
5.如果有一个操作数是NaN,则返回NaN;
6.如果有一个操作数是undefined,则返回undefined;
逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于逻辑与操作而言,如果第一个操作数是false,则无论第二个操作数是什么值,结果都不再可能是true了。来看下面的例子:
var found=true;
var result=(found && someUndefinedVariable) //这里会发生错误
alert(result) //这一行不会被执行
在上面的代码中,当执行逻辑与会发生错误,因为变量someUndefinedVariable没有声明。由于变量found的值是true,所以逻辑与操作符会继续对变量someUndefinedVariable求值。但someUndefinedVariable尚未定义,因此就会导致错误。这说明不能逻辑与操作中使用未定义的值。
var found=false;
var result=(found && someUndefinedVariable) //不会发生错误
alert(result) //会被执行
逻辑或与之相反
相等操作符
null==undefined //true
'NaN'==NaN //false
5==NaN //false
NaN==NaN //false
NaN!=NaN //true
false==0 //false
true==1 //true
true==2 //false
undefined==0 //false
null==0 //false
'5'==5//true
全等和不全等
var result1=("55" == 55); //true 因为转换后相等
var result2=("55" === 55); //false 因为不同的数据类型不想等
var result1=("55" != 55); //false 因为转换后相等
var result2=("55" !== 55); //true 因为不同的数据类型不想等
记住:
null==undefined //true 因为它们是类似的值
null===undefined //false 因为它们是不同类型的值
条件操作符
variable = boolean_expression ? true_value : false_value
检测类型
typeof "Nicholas" //string
typeof null //object
typeof new Object()//object
虽然在检测基本数据类型时typeof是非常得力的助手,但在检测引用类型的是值时,这个操作符的用处不大。通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此,ECMAscript提供了instanceof操作符,其语法如下:
person instanceof Object //true
colors instanceof Array //true
传递参数
function addTen(){
num+=10;
return num;
}
var count=20;
var result=addTen(count)
alert(count) //20,没有变化
alert(result) //30
function setName(obj){
obj.name="Nicholas"
}
var person=new Object();
setName(person);
alert(person.name); //Nicholas
function setName(obj){
obj.name="Nicholas"
obj=new Object()
obj.name="Greg"
}
var person=new Object();
setName(person);
alert(person.name); //Nicholas
以上代码中创建一个对象,并将其保存在了变量person中。然后,这个变量被传递到setName函数中之后就被复制给了obj。在这个函数内部,obj和erson引用的是同一个对象。换句话说,即使这个变量是按值传递的,obj也会按引用来访问同一对象
引用类型:
Object类型
一般来说,访问对象属性时使用的都是点表示法,这也是很多面向对象语言中的通用的语法。不过,在javacript也可以使用方括号表示法来访问对象属性。在使用方括号语法时,应该将要访问的属性以字符串的形式放在方括号中,如下面的例子所示:
person["name"] //Nicholas
person.name //Nicholas
var propetyName="name"
person[propetyName] //Nicholas
person["first name"] = 'Nicholas'
由于first name中包含一个空格,所以不能使用点表示法来访问它。然而,属性名中是可以包含非字母非数字的,这时候就可以使用方括号表示法来访问它们
Array类型
数组的length属性很有特点,它不是只读的。因此,通过设置这个属性,可以从数组的末尾移除或向数组中添加新项。
var colors=["red","green","yellow"]
colors.length=2;
alert(colors[2]) //undefined
var colors=["red","green","yellow"]
colors.length=4;
alert(colors[3]) //undefined
检测数组
Array.isArray(value) //true or false
栈方法
var colors=["red","green","yellow"]
colors.push("black")
var item=colors.pop() //取得最后一项
alert(item) //"black"
colors.length=2
var item=colors.shift() //取得第一项
alert(item) //"red"
colors.length=2
colors.unshift("black") //跟push相反
push!=unshift
pop!=shift
重排序方法
reverse()和sort()
arr.reverse()
function compare(val1,val2){
if(val1<val2){return -1}
else if(val1>val2){return 1}
else{return 0}
}
//升序
function compare(val1,val2){
if(val1<val2){return 1}
else if(val1>val2){return -1}
else{return 0}
}
//降序
var values=["0","1","5","15","10"]
values.sort(compare);
alert(values) //0,1,5,10,15
操作方法
concat() 合并数组
arr1.concat(arr2)
slice()
var colors=["yeloow","red","black","blue","pink"]
colors.slice(1) // red,black,blue,pink
colors.slice(1,4) //red,black,blue //如果有两个参数,该方法返回起始和结束位置之间的项,但不包括结束位置的项
splice()
删除:可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数。例如,splice(0,2)会删除数组中的前两项
插入:可以向指定位置插入任意数量的项,只需提供3个参数,起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传第四、第五,以至任何多个项。例如,colors.splice(2,0,"red","yellow") 会从当前数组的位置2开始插入字符串red和yellow
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必和删除的项数相等。例如,splice(2,1,"red","yellow")会删除当前数组位置2的项,然后再从位置2开始插入字符串red和yellow
位置方法
indexOf() //从头开始找
lastIndexOf() //末尾开始找
var numbers=["1","2","3"]
alert(numbers.indexOf(2)) //1 如果是-1 就是找不到
迭代方法
every()
filter()
forEach()
map()
some()
var numbers=["1","2","3","4","5","4","3","2","1"]
var everyResult=numbers.every(function(item,index,array){
return(item>2)
})
alert(everyResult) //false //并不是每个都大于2
var someResult=numbers.some(function(item,index,array){
return(item>2)
})
alert(someResult) //true //有些是大于2
var filterResult=numbers.filter(function(item,index,array){
return(item>2)
})
alert(filterResult) //[3,4,5,4,3]
var mapResult=numbers.map(funciton(item,index,array){
return item *2
})
alert(mapResult) //[2,4,6,8,10,8,6,4,2]
numbers.forEach(function(item,index,array){
alert(item)
})
RegExp实例方法
RegExp对象的主要方法是exec,exec接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。返回的数组虽然是Array的实例,但包含了两个额外的属性:index和input。其中index表示匹配项在字符串中的位置,而input表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串。
var text="mom and dad and baby";
var pattern=/mom( and dad( and baby)?)?/gi;
var matches=pattern.exec(text)
console.log(matches.index) //0
console.log(matches.input) //mom and dad and baby
console.log(matches)
正则表达式的第二个方法是test,它接受一个字符串参数。在模式与该参数匹配的情况下返回true,否则返回false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便。因此,test方法经常被用在if语句中,如下所示
var text="000-000-000";
var parttern=/\d{3}-\d{2}-\d{4};
if(parttern.test(text)){
console.log("the parttern was mathed")
}
Function类型
function num(num1,num2){
return num1+num2;
}
var num=function(num1,num2){
return num1+num2;
}
函数声明与函数表达式
函数声明调用不分先后
sum(10,10);
function sum(num1,num2){
return num1+num2
}
以上代码完全可以正常运行
sum(10,10);
var sum=function(num1,num2){
return num1+num2;
}
以上代码会在运行期间产生错误
作为值的函数
排序实例
function creatComparisonFunction(propertyName){
return function(obj1,obj2){
var value1=obj1[propertyName];
var value2=obj2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1
}else{
return 0;
}
}
}
var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(creatComparisonFunction("name")) //根据名字排序
data.sort(creatComparisonFunction("age")) //根据年龄排序
Number类型
var num =10;
num.toFixed(2); //10.00
String类型
var str="hello";
var result=str.concat("word")
var str="hello world";
str.slice(3); // lo world
str.substring(3); // lo world
str.substr(3); // lo world
str.slice(3,7); // lo w 这个7是结束第7个,不包含7
str.substring(3,7); // lo w 这个7是结束第7个,不包含7
str.substr(3,7); // lo worl 这个7是要返回的个数
转换大小写
var str="hello world"
console.log(str.toLocaleUpperCase()) //HELLO WORLD
console.log(str.toUpperCase()) //HELLO WORLD
console.log(str.toLocaleLowerCase()) //hello world
console.log(str.toLowerCase()) //hello world
查找
var text="cat,bat,sat,fat";
var pos=text.search(/at/);
console.log(pos) //1
替换
var text="cat,bat,sat,fat";
var result=text.replace("at","ond")
console.log(result) //cond,bat,sat,fat
result=text.replace(/at/g,"ond")
console.log(result) //cond,bond,sond,fond
var colorText="red,yellow,blue,pink";
colorText=colorText.split(",");//["red", "yellow", "blue", "pink"]
colorText=colorText.split(",",2);//["red", "yellow"]
console.log(colorText)
var max=Math.max(3,54,32,16);
var min=Math.min(3,54,32,16);
console.log(max) //54
console.log(min) //3
Math.ceil() //向上舍入
Math.floor() //向下舍入
Math.round() //正常舍入
Math.random() //返回大于等于0小于1的随机数
每个函数都包含两个非继承而来的方法:apply()和call()
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象的值。
首先apply方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。
其中,第二个参数可以是Array的实例,也可以argumenst对象;例如:
function sum(num1,num2){
return num1+num2
}
function callSum1(num1,num2){
return sum.apply(this,arguments); //此方法等价于return this.sum(num1,num2);
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2])
}
console.log(callSum1(10,10)) //20
console.log(callSum2(2,2)) //4
call()方法与apply()方法的作用相同,他们的区别仅在于接收参数的方式不同。对于call方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call方法时,传递给函数的参数必须逐个列举出来,如下面的列子所示
function sum(num1,num2){
return num1+num2
}
function callSum1(num1,num2){
return sum.call(this,num1,num2); //此方法等价于return this.sum(num1,num2);
}
console.log(callSum1(10,10)) //20
事实上,传递参数并非apply和call真正的用武之地;它们真正强大的地方是能够扩充函数赖以生存的作用域,下面来看一个例子:
window.color="red";
var o={color:"blue"};
function sayColor(){
console.log(this.color)
}
sayColor() //red
sayColor.call(this) //red
sayColor.call(window) //red
sayColor.call(o) //blue
但是当运行sayColor.call(o)时,函数的执行环境就不一样了,因此此时函数体内的this对象指向了o,于是结果显示是blue
面向对象程序设计
创建对象
1.工厂模式
function creatPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
console.log(this.name);
}
return o
}
var person1=creatPerson("zuozuo",23,"IT");
person1.sayName()
2.构造函数模式
构造函数可用来创建特定类型的对象。像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name)
}
}
var person1=new Person("zuo",33,"IT");
person1.sayName()
没有显式的创建对象
直接将属性和方法赋给了this对象
没有return语句
按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
这个做法借鉴其他OO语言,主要是为了区别与ECMAScript中的其他函数,因为构造函数本身也是一个函数,只不过可以用来创建对象而已。
要创建Person的新实例,必须使用new操作符
这两个对象都有一个constructor属性,该属性指向Person
任何函数,只要通过new操作符来调用,那它就可以作为构造函数
3.原型模式
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
function Person(){
}
Person.prototype.name="zuo";
Person.prototype.age=33;
Person.prototype.job="ITP";
Person.prototype.sayName=function(){
console.log(this.name)
}
var person1=new Person();
person1.sayName()
var person2=new Person();
person2.sayName();
console.log(person1.sayName==person2.sayName) ///true
更简单的原型语法
function Person(){
}
Person.prototype={
name:"zuo",
age:33,
job:'sdf',
arr:["1","2"],
sayName:function(){
console.log(this.name)
}
}
var person1=new Person();
person1.name='sfdasfdafasf'
person1.sayName()
person1.arr.push("111111")
person1.arr=["5555"]//这个方法并不会影响person2.arr的值
console.log(person1.arr)
var person2=new Person();
person2.sayName();
console.log(person1.sayName==person2.sayName) ///true
console.log(person2.arr)
继承
ECMAScript实现继承主要是依靠原型链来实现的。
原型链:
利用原型让一个引用类型继承另一个引用类型的属性和方法。
简单回顾以下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.subproperty;
}
var instance=new SubType();
console.log(instance.getSuperValue()) //true
console.log(instance.getSubValue()) //false
递归:
递归函数是在一个函数通过名字调用自身的情况下构成的
匿名函数的基本形式为(function(){...})();
前面的括号包含函数体,后面的括号就是给匿名函数传递参数并立即执行之
匿名函数的作用是避免全局变量的污染以及函数名的冲突
(function(x,y){
alert(x+y);
return x+y;
}(3,4));
闭包:
闭包是指有 权访问另一个函数作用域中的变量 的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
document.getElementById("myDiv")
var images=document.getElementByTagName("img")
var radio=document.getElementByName("color")
var div=document.getElementById("myDiv");
console.log(div.tagName);
console.log(div.tagName==div.nodeName)
html元素
id
title
lang
dir
className
var div=document.getElementById("myDiv");
console.log(div.id);//myDiv
console.log(div.className);//bd
console.log(div.title);//leiei
console.log(div.lang);//en
console.log(div.dir);//ltr
取得特性:
操作特性的DOM方法主要有三个,分别是getAttribute()、setAttribute()和removeAttribute();
<div id="myDiv" class="bd" userid="1"></div>
var div=document.getElementById("myDiv");
console.log(div.getAttribute("id"))
console.log(div.getAttribute("class"))
console.log(div.getAttribute("userid"))
var div=document.getElementById("myDiv");
div.setAttribute("class","test")
div.setAttribute("userid","22")
console.log(div.getAttribute("class"))
创建元素
var div=document.createElement("div");
div.id="myDdiv";
div.className="test";
document.body.appendChild(div)
元素的子节点
<ul id="myList">
<li userId="1">1</li>
<li userId="2">2</li>
<li userId="3">3</li>
</ul>
var ul=document.getElementById("myList");
for(var i=0,len=ul.childNodes.length;i<len;i++){
if(ul.childNodes[i].nodeType==1){
console.log(ul.childNodes[i].getAttribute("userId"))
}
}
DOM扩展
选择符API
1.querySelector()
<div class="selected"></div>
<div id="myDiv">asdfasfas</div>
var body=document.querySelector("body");
var myDiv=document.querySelector("#myDiv")
var selected=document.querySelector(".selected")
console.log(selected)
2.querySelectorAll()
var myList=document.querySelectorAll("li")
for(var i=0;i<myList.length;i++){
console.log(myList[i].className)
console.log(myList[i].getAttribute("userId"))
}
<ul id="myList">
<li userId="1" class="tt">1</li>
<li userId="2" class="88">2</li>
<li userId="3" class="kk">3</li>
</ul>
元素遍历
<div class="1"></div>
<ul id="myList">
<li userId="1" class="tt">1</li>
<li userId="2" class="88">2</li>
<li userId="3" class="kk">3</li>
</ul>
<div class="2"></div>
var myList=document.getElementById("myList")
console.log(myList.childElementCount) //3
console.log(myList.firstElementChild)//<li userId="1" class="tt">1</li>
console.log(myList.lastElementChild)//<li userId="3" class="kk">3</li>
console.log(myList.previousElementSibling)//<div class="1"></div>
console.log(myList.nextElementSibling)//<div class="2"></div>
getElementByClassName("selected")
classList属性
<div class="bd user disbaled" id="classList"></div>
var div=document.getElementById("classList")
div.classList.remove("user");
div.classList.add("current");
div.classList.toggle("user");
if(div.classList.contains("bd")&&!div.classList.contains("disbaled")){
//执行操作
}
自定义数据属性
<div id="myDiv" data-app="111" data-myname="zuozuo">顺丰到付</div>
var div=document.getElementById("myDiv")
alert(div.innerText) //顺丰到付 获取dom的text
var app=div.dataset.app;
var myname=div.dataset.myname;
console.log(app) //111
console.log(myname) //zuozuo
div.dataset.app="222";
div.dataset.myname="leiei";
if(div.dataset.myname){
console.log("hello " + div.dataset.myname) //hello leiei
}
children属性==childNodes
访问元素样式
var div=document.getElementById("myDiv")
div.style.backgroundColor="red";
div.style.width="200px"
div.style.border="#000 solid 1px"
事件
1.事件冒泡---从下往上执行 div-body-html-document
2.事件铺货---从上往下执行 document-html-body-div
HTML事件处理程序
<input type="text" value="sle" onclick="submit()">
<script>
function submit(){
console.log("onclick")
}
</script>
DOM0级事件处理程序
var btn=document.getElementById("myBtn")
btn.onclick=function(){
console.log(this.value)
}
DOM2级事件处理程序
var btn=document.getElementById("myBtn")
var handler=function(){
console.log(this.value)
}
btn.addEventListener("click",handler,false)
btn.removeEventListener("click",handler,false)
IE事件处理程序
var btn=document.getElementById("myBtn")
var handler=function(){
console.log(this.value)
}
btn.attachEvent("click",handler,false)
btn.detachEvent("click",handler,false)
跨浏览器的事件处理程序
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
}
}
var btn=document.getElementById("myBtn")
var handler=function(){
console.log(this.value)
}
EventUtil.addHandler(btn,"click",handler)
EventUtil.removeHandler(btn,"click",handler)
事件对象
在需要通过一个函数处理多个事件时,可以使用type属性
var handler=function(event){
switch(event.type){
case "click":
console.log(1)
break;
case "mouseover":
console.log(2);
break;
case "mouseout":
console.log(3);
break;
}
}
btn.click=handler;
btn.onmouseouver=handler;
btn.onmouseout=handler
直接添加到一个按钮的事件处理程序可以调用stopPropagation(),从而避免出发注册在document.body上面的事件处理程序
btn.onclick=function(event){
console.log(1);
event.stopPropagation();
}
document.body.onclick=function(event){
console.log(2)
}
阻止链接导航这一默认行为,那么通过链接的onclick事件处理程序可以取消它
btn.onclick=function(event){
event.preventDefault()
}
跨浏览器的事件对象
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
getEvent:function(event){
return event?event:window.event
},
getTarget:function(event){
return event.target || event.srcElement
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault;
}else{
event.returnValue=false;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation
}else{
event.cancelBubble=true;
}
}
}
var btn=document.getElementById("myBtn")
var handler=function(){
console.log(this.value)
}
EventUtil.addHandler(btn,"click",handler)
EventUtil.removeHandler(btn,"click",handler)
btn.onclick=function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
}
link.onclick=function(event){
event=EventUtil.getEvent(event);
EventUtil.preventDefault(event);
}
btn.onclick=function(event){
event=EventUtil.getEvent(event);
EventUtil.stopPropagation(event);
}
document.body.onclick=function(event){
console.log(11)
}
表单
每个表单都有elements属性
<form action="" id="myForm">
<ul>
<li>
<input type="radio" name="color" value="red">
</li>
<li>
<input type="radio" name="color" value="yellow">
</li>
<li>
<input type="radio" name="color" value="black">
</li>
</ul>
</form>
var form=document.getElementById("myForm");
var colorFields=form.elements["color"];
console.log(colorFields)