javascript笔记
初识javascript注意:严格区分大小写,且以;结尾。
调试程序:console.log()代替alert()。
数据类型:typeof(变量)测试数据类型
1.Number(不区分整数和浮点数)
NaN表示Not a Number
Infinity表示当数值超过了js所定义的最大值
2.字符串string
如果字符串内包含‘ ‘’ 可以用转义字符表示 \'。
多行字符串如果不想写\n可以使用反引号
`这是
一个
多行字符串`;
字符串链接用+号:‘你好,’+name+‘,你今年’+age+'岁了!';
ES6新增模板变量,实现字符串连接: `你好,${name},你今年${age}岁了!`使用反引号ESC下的那个键。
常用属性length: var a="hello"; a.lehgth;//5
要获取字符串的某个指定位置的字符可以使用下标,索引越界一律返回undefind
常用方法:调用方法本身不会改变原有字符串内容,而是返回一个新字符串
toUpperCase()把一个字符串全部变成大写
toLowerCase()把一个字符串全部变成小写
indexOf(‘hello’)会搜索指定子串出现的位置//返回索引值,若未找到返回-1
substring(0,5)返回指定索引区间的子串,从0开始返回5个字符,若只有一个参数则从索引开始到最后结束。
3.布尔值boolean
真假判断:
真的:true、非零数字、非空字符串、非空对象
假的:false、数字零、空字符串、空对象、undefined
4.object
5.function
不包含任何值的数据类型:
1.null表示一个空值,和0,''不同,0是一个数值;''是一个空串 var t=null;
var ee=null; typeof(ee) //object 面试题
2.undefined未定义 var a;
3种对象类型:
1.Object(是由键-值组成的无序集合)
var person={
name:'BOb',
age:20,
tags:['js','web','mobile'],
hasCar:true,
'middle-school':'No.1 Middle School'
};
javascript对象的键都是字符串类型,值可以是任意类型;每个键又称为对象的属性
获取对象属性用:对象变量.属性名 person.name;//'Bob'
如果属性名包含特殊字符,就必须用''括起来,例如‘middle-school’,访问这个属性需要['middle-school']来访问
person['middle-school']
为对象添加属性:person.birth=1998;
删除对象属性: delete person.age;
检测person是否拥有某一属性:'age' in person;//true 继承得属性也包含
只检测person自身拥有的属性hasOwnProperty() person.hasOwnProperty('age');//true
2.Date
3.Array(js数组可以包含任意数据类型)
创建数组方法 var arr=new Array(1,2,3);
var arr=[1,2,3];//出于代码可读性,建议使用[]
数组长度:length
直接给Array的length赋一个新值会改变Array的大小,变大则其他值未定义,变小则省略数组后面的值,通过索引赋值,索引越界也会改变数组的大小(其他编程语言下会报错,建议不要直接修改数组的大小也不要数组越界)
遍历数组:for(var i=0;i<arr.length;i++){
console.log(arr[i]);
}
for(var i in arr){
console.log(arr[i]);
}//请注意for in得到的键值是String而不是number
常用方法:indexOf(10)搜索一个指定元素的位置,同String用法
slice(0,3)截取Array部分元素,等同于String的substring();如果不传递任何参数,他会从头到尾截取所有元素,相当于复制一个Array。
var arr=[1,2,3];
var aCopy=arr.slice();
arr===sCopy; //false
var arr2=arr;
arr===arr2; //true
添加删除元素:
push()向Array末尾添加若干元素 push('A','B');
pop()删除Array最后一个元素 arr.pop();
unshift()向Array的头部添加若干元素 unshift('A','B');
shift()把Array的第一个元素删掉 arr.shift();
splice插入、删除、替换(可以从中间),它可以从指定的元素索引开始删除若干元素,然后再从该位置添加若干元素。
var arr=[1,2,90,33,76,4];
arr.splice(2,2,'one','two');//从索引2开始删除2个元素,再添加2个元素[1,2,'one','two',76,4]
arr.splice(2,2);//只删除不添加
arr.splice(2,0,'one','two');//只添加不删除
对原arr进行了改变;
排序: sort()对Array()进行排序,只能排序字符串
var arr=[‘flo’,’sdf’,’awe’,’bsad’]; arr.sort();//从a到z
如果排序数字或其他//添加一个比较函数
arr.sort(function (num1,num2){
return num1-num2;//从小到大
num2-num1;//从大到小
})
reverse()对字符串进行倒叙排 arr.reverse();//从z到a
连接: concat()把当前的Array和另一个Array连接起来,并返回一个新的Array
var arr1=[1,2,3,4];
var arr2=[5,6,7,8];
alert(arr1.concat(arr2));//此时这是一个新的数组, 弹出 1,2,3,4,5,6,7,8
console.log(arr1.concat(5,6,[7,8]));
join()把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串(把数组变成字符串)
var arr=[1,2,8,34,3];
console.log(arr.join('-'));
split()把字符串分隔成数组
var arr='1-2-3-4-onr';
console.log(arr.split('-'));//['1','2','3','4','onr']
运算符:
注意相等运算符:==和===
==会自动转换数据类型在比较
===不会自动转换数据类型(建议使用)
特殊NaN与所有其他值都不相等包括他自己
NaN===NaN //false
判断NaN通过isNaN()
isNaN(NaN) //true
两个浮点数比较是否相等,只能计算他们之差的绝对值是否小与某个值
Math.abs(1/3-(1-2/3))<0.00000001; //true
a++ a -- ++a --a 如果没有赋值则运算结果是一样的
如果有赋值运算 a++ 先将a赋值给前面的变量 在+1,++a自身先+1再赋值给变量
var a=10; var b=a++; //b=10,a=11
var c=++a //c=11,a=11
优先级:算数运算符 >关系运算符 (比较运算符)>逻辑运算符
变量:不用var申明的变量会被视为全局变量,为了避免所以使用strict模式
在js代码的第一行 ‘use strict’;
作用域:如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
如果内部函数和外部函数的变量名重名:JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
规范:我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var
申明函数内部用到的所有变量:
function foo(){
var
x=1,//x初始化为1
y=x+1,//y初始化为2
z,1;//z和i为undefind
//其他语句
}
全局作用域:不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window
,全局作用域的变量实际上被绑定到window
的一个属性:
var course = 'Learn JavaScript';
alert(course); // 'Learn JavaScript'
alert(window.course); // 'Learn JavaScript' 直接访问全局变量
course
和访问window.course
是完全一样的。
顶层函数(即window.onload之前定义的函数)的定义也被视为一个全局变量,并绑定到window
对象:
命名空间: 不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突;减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。
例如:<script src=
"1.js"
></script>
<script src=
"2.js"
></script>
<script type=
"text/javascript"
>
DOC.hello();
ZXD.hello();
</script>
<script>//1.js
var
DOC = DOC || {};
//若全局空间中已有同名对象,则不覆盖该对象;否则创建一个新的命名空间。
DOC.hello =
function
() {
console.log(
"hello DOC"
);
}
var
ZXD = ZXD || {};
ZXD.hello =
function
() {
console.log(
"hello ZXD"
);
}
for
循环等语句块中是无法定义具有局部作用域的变量的:function foo() {
var sum = 0;
for (let i=0; i<100; i++) {//i被限制在当前代码块
sum += i;
}
i += 1;//i作用域失效,如果for (var i=0; i<100; i++)则可以继续调用
}
const PI = 3.14;
console.log(a,b);//a=1,b=7
[]
括起来。
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
解构对象:
使用解构赋值,便于获取对象的指定属性:
var person={
name:'zhangsan',
age:20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school',
address: {
city: 'Beijing',
street: 'No.1 Road',
zip: '100001'
}
};
var {name, age, passport} = person;
对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的:
var {name, address: {city, zip}} = person;
console.log(name,city,zip);//zhangsan Beijing 100001
无声明赋值:
var a,b;
({a,b}={a:1,b:2});//必须使用()不然会被当成一个语句块
给新的变量名赋值:
var {a:aa=10,b:bb=5}={a:3};
console.log(aa,bb);//3,5
使用场景:快速获取当前页面的域名和路径:
var {hostname:domain, pathname:path} = location;
如果一个函数接收一个对象作为参数,可以使用解构直接把对象的属性绑定到变量中,例如,快速创建一个Date对象
function buildDate({year, month, day, hour=0, minute=0, second=0})
{ return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second); }
条件判断:如果某个条件成立,则后续就不再继续判断了
循环:for循环通常利用索引遍历数组
for in它可以把一个对象的所有属性依次循环出来
for(var key in person){
console.log(key);
}
集合类型:Map是一组键值对的结构,具有极快的查找速度
初始化Map需要一个二维数组:
var m=new Map([['Michael',95],['Bob',75],['Tracy',85]]);
初始化一个空Map:
var m=new Map();//空Map
m.set('Adam',67);//添加新的key-value
m.has('Adam');//是否存在key 'Adam' 返回true
m.get('Adam');//67 获取对应的value值
m.delete('Adam');//删除key 'Adam'
由于一个key只能对应一个value,所以多次对一个key放入value后面的值会把前面的值冲掉
Set是一组key的集合,但不存储value且key不能重复
创建set,需要提供一个Array作为输入:
var s=new Set([1,2,3,4]);
直接创建一个空Set:
var s=new Set();
添加元素到Set: s.add(5);
删除元素: s.delete(3);
iterable:ES6标准新引入的类型,包括Map Set Array,具有iterable类型的集合可以通过for...of(ES6)循环遍历
var a=['A','B',"C"];
var m=new Map([[1,'x'],[2,'y'],[3,'z']]);
var s=new Set(['A','B','C']);
for(var i of a){
console.log(i);
}
for(var i of s){
console.log(i);
}
for(var i of m){
console.log(i);
}
forEach()iterable内置方法,它接收一个函数,每次迭代就自动回调该函数
以Array为例: var a=['A','B','C'];
a.forEach(function(element,index,array){
//element代表Array元素值
//index代表索引值
//array代表Array对象本身
console.log(index+':'+element);//0:A,1:B,2:C
});
以Set为例:Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身
var s=new Set(['A','B','C']);
s.forEach(function(element,index,array){
//element和index都是元素本身
console.log(element);
});
以Map为例: var a=new Map([[1,'A'],[2,'B'],[3,'C']]);
a.forEach(function(value,key,map){//不需要的参数可以不用传递,例如只传递value
console.log(key,value);//1"A",2"B",3"C"
});
函数:
函数的定义:两种定义完全等价
(1)function abs(x){
if(x>=0){
return x;
}else{
return -x;
}
}
(2) var abs=function(x){
f(x>=0){
return x;
}else{
return -x;
}
};//这种情况下function是一个匿名函数,它没有函数名,把它赋值给了变量abs,通过abs就可以调用该函数,此写法末尾加;,表示赋值语句结束。
关键字 arguments:它只在函数内部起作用,永远指向当前函数调用传入的所有参数。含所有参数的一个数组。
由于js允许传入任意个参数而不影响调用,参数或多或少都没关系
例子:获取元素当前样式
<style>
#div1{
background-color: red;
width: 100px;
height: 100px;
}
</style>
<script>
window.onload=function(){
var oDiv=document.getElementById('div1');
var oBtn=document.getElementById('btn1');
oBtn.onclick=function(){
css(oDiv,'backgroundColor','green');//复合样式的写法backgroundColor
console.log(oDiv.style.backgroundColor);//输出改变后的样式,说明设置的是行内样式,优先级高
}
var css=function(obj,attr,value){//判断css()是获取还是设置
if(arguments.length===2){//如果css()传递的是两个参数,获取样式
console.log(getStyle(obj,attr));
}else if(arguments.length===3){//如果css()传递的是三个参数,将设置样式
obj.style[attr]=value;
}
}
function getStyle(obj,attr){//获取样式函数
if(obj.currentStyle){
return obj.currentStyle[attr];//obj.currentStyle[‘width’]===obj.currentStyle.width;因为传递的参数是字符串‘width’,obj.currentStyle.’width’是错误的,所以用obj.currentStyle[attr]
}else{
return getComputedStyle(obj,false)[attr];
}
}
}
</script>
<body>
<div id="div1"></div>
<input id="btn1" type="button" value="改变" />
</body>
如果获取除了已定义参数之外的参数,用rest参数(ES6标准)
function foo(a,b,...rest){
console.log(rest);
}
在一个对象中绑定函数,称为这个对象的方法:
例如:(1)var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age();//调用
(2)function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 28, 正常结果
getAge(); // NaN,this指当前对象,此时的this指向全局对象window
要保证this
指向正确,必须用obj.xxx()
的形式调用!
这种情况在strict模式下让函数的this
指向undefined
(3)对于下面这种情况this
指针只在age
方法的函数内指向xiaoming
,在函数内部定义的函数,this
又指向undefined
了
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获thi;这种方法的好处是,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age(); // 28
(4)apply
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空;第一个参数就是需要绑定的
this
变量指向哪个对象,第二个参数是Array
,表示函数本身的参数。
另一个与apply()
类似的方法是call()
,唯一区别是:
apply()
把参数打包成Array
再传入;Math.max.apply(null, [3, 5, 4]); // 5
call()
把参数按顺序传入。Math.max.call(null, 3, 5, 4); // 5
对普通函数调用,我们通常把this
绑定为null
。
装饰器 apply()
,我们还可以动态改变函数的行为。
例如:'use strict';
var count = 0;
var oldParseInt = parseInt; // 保存原函数;实际上parseIn是一个函数名,parseIn()在一个函数名后面加了括号就表示执行该函数了。引用一个函数,只需一个函数名(没有后面的括号)
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数;arguments 是javascript内置的关键字,表示函数传入的参数值。
};
// 测试:
parseInt('10');
parseInt('20');
parseInt('30');
console.log('count = ' + count); // 3
高阶函数:一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
例如:'use strict';
function add(x, y, f) {
return f(x) + f(y);
}
var x = add(-5, 6, Math.abs); // 11
console.log(x);
map()返回一个新的数组,不会改变原始数组;其结果是该数组中的每个元素都调用一个提供的函数后返回的结果,也就是每个元素都调用下列的pow(x);
例如:'use strict';
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow);// [1, 4, 9, 16, 25, 36, 49, 64, 81]
console.log(results);
列如:把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT']
,输出:['Adam', 'Lisa', 'Bart']
。
function normalize(arr) {
return arr.map(function(x) { return x[0].toUpperCase() + x.slice(1).toLowerCase(); });
}
if (normalize(['adam', 'LISA', 'barT']).toString() === ['Adam', 'Lisa', 'Bart'].toString()) {
console.log(normalize(['adam', 'LISA', 'barT']).toString());
console.log('测试通过!');
}else {
console.log('测试失败!');
}
reduce()接收一个函数作为累加器
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
例如:把一个字符串13579
先变成Array
——[1, 3, 5, 7, 9]
,再利用reduce()
就可以写出一个把字符串转换为Number的函数。
练习:不要使用JavaScript内置的parseInt()
函数,利用map和reduce操作实现一个string2int()
函数:
(1)function stringInt(s) {
return s.split('').map(function(x){return +x}).reduce(function(x,y){ return x*10+y});
}//+操作符可以将字符串转换为数字,+123,返回123
//测试
if (stringInt('0') === 0 && stringInt('12345') === 12345 && stringInt('12300') === 12300) {
if (stringInt.toString().indexOf('parseInt') !== -1) {
console.log('请勿使用parseInt()!');
} else if (stringInt.toString().indexOf('Number') !== -1) {
console.log('请勿使用Number()!');
//parseInt(string,radix);第二个参数表示进制
} else {
console.log('测试通过!');
}
}else {
console.log('测试失败');
}
(2)function stringInt(s){
var arr=s.split('');//字符串转换为数组
var newArr=arr.map(function(x){
return x*1;//隐士类型转换
})
//var newArr=arr.map(Number);//'123a' parseInt返回123,Number返回NaN; 'a123'parseInt返回NaN
//varnewArr=arr.map(parseInt);//返回1,NaN,NaN;map回调函数有3个参数,callback(currentValue, index, array),通常我们仅需要第一个参数,而忽略了传入的后面两个参数。不幸的是,parseInt(string, radix)
没有忽略第二个参数,导致实际执行的函数分别是:
parseInt('0', 0); // 0, 按十进制转换
parseInt('1', 1); // NaN, 没有一进制
parseInt('2', 2); // NaN, 按二进制转换不允许出现2
var newArr=arr.map(Number) 因为Number(value)
函数仅接收一个参数
var res=newArr.reduce(function(x,y){
return x*10+y;
});
return res;
};
Dom:document object model 文档对象模型,操纵页面的
获取节点:
getElementById()通过id
getElementsByTagName()通过标签
getElementsByName()通过元素的name属性
节点指针:
childNodes获取子节点列表,兼容性问题:IE6-8返回的是元素节点,其他浏览器返回元素节点和文本节点
nodeType
children每个浏览器返回的都是元素节点