JavaScript笔记

01-函数

一.创建一个函数对象的三种方法

        1.构造函数法

var fun = new Function ("console.log('Hello,这是我创建的第一个函数对象');");

        2.使用函数声明来创建一个函数

function 函数名 (形参1,形参2...形参n)
{

}

        3.使用函数表达式

匿名函数的形式,是重要的函数式接口

var 函数名 = function (形参1,形参2...形参n)
{

}

二.函数的参数和返回值

函数的参数

  1. 在函数 ( ) 中指定形参时不需要指定数据类型
  2. 函数的实参可以是任意的数据类型,也可以是一个对象

当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递

函数的实参也可以是一个函数

function fun(a)
{
    console.log("a = "+a);
}

fun( mianji() ); 调用函数
//相当于使用的函数的返回值
fun( mianji );  函数对象
//相当于直接使用函数对象
  1. 调用函数时不会检查实参的类型,所以要注意是否有可能会接收到非法的参数
  2. 调用函数时不会检查实参的数量,多余实参不会被赋值
  3. 如果实参的数量少于形参的数量,则没有对应实参的形参是undefined,输出将是NaN

定义一个函数,判断一个数字是否是偶数,如果是返回true,否则返回false

function is_oushu(x)
{
    if(x % 2 == 0) return true;
    else return false;
}

以上代码还可以更优雅 

function is_oushu(x)
{
    return x % 2 == 0;
}

函数的返回值

return 后的语句都不会执行

return后可以跟任意类型的值

  • 如果return语句后不跟任何值就相当于返回一个undefined
  • 如果函数中不写return,则也会返回undefined

三.立即执行函数  

只会执行一次

(function()
{
    alert("我是一个匿名函数~~~~");
})();

四、函数对象的方法   

  1. call()        可以将实参在对象之后依次传递
  2. apply()       需要将实参封装到一个数组中统一传递
fun.call(obj,2,3);
fun.apply(obj,[2,3]);

这两个方法都是函数对象的方法,需要通过函数对象来调用

当对函数调用call()和apply()都会调用函数执行

在调用call()和apply()可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this

五、arguments 

在调用函数时,浏览器每次都会传递进两个隐含的参数

  1. 函数的上下文对象this
  2. 封装实参的对象arguments 

        arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度

        在调用函数时,我们所传递的实参都会在arguments中保存

        arguments.length可以用来获取实参的长度 

我们即使不定义形参,也可以通过arguments来使用实参 


 arguments里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象

 02-方法

对象的属性也可以是函数

如果一个函数作为一个对象的属性保存,那这个函数就是这个对象的方法

调用这个函数就是调用对象的方法 

03-枚举对象中的属性

 for...in 语句对象中有几个属性,循环体就会执行几次

每次执行时,会将对象中的一个属性名字赋值给变量

for ( var 变量 in 对象) {

}


for ( var n in obj) {
    console.log(obj.n);
}

 以上代码只会输出undefined,因为obj这个对象中没有n这个属性

如果想要访问的属性是个变量,则需使用中括号访问

console.log(obj[n]);

 04-作用域

 作用域指一个变量作用的范围 

一、全局作用域 

全局作用域在页面打开时创建,在页面关闭时销毁

在全局作用域中有一个全局对象window

        它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用 

在全局作用域中

  •  创建的变量都会作为window对象的属性保存
  •  创建的函数都会作为window对象的方法保存 

全局作用域中的变量都是全局变量,在页面的任何部分都可以访问的到 

1.变量的声明提前 
console.log("a = "+a);
var a = 123;
var a;
console.log("a = "+a);
a = 123;

以上两段代码输出都是 a = undefined

因为使用var关键字声明的变量,会在所有代码执行之前被声明 (但是不会被赋值)

但是如果声明变量时不使用var关键字,则变量不会被声明提前

2.函数的声明提前
  • 使用函数声明创建的函数会在所有代码执行之前就被创建,所以我们可以在函数声明前来调用函数
  • 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用 

 二、函数作用域

调用函数时创建函数作用域,函数执行完毕后,函数作用域销毁

每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的 


 在函数作用域中可以访问到全局作用域的变量,反之则不然

var a = 10;
function fun(){
    console.log("a = "+a);
}

fun();

 如果有变量重复赋值,在函数内外调用时遵循就近原则(这里的近指的是层数)


 在函数中想要访问全局变量可以使用window

在函数作用域也有声明提前的特性

在函数中不使用var声明的变量都会成为全局变量 

定义形参相当于在函数作用域中声明了变量

var e = 23;
function fun(e)
{
    alert(e);
}

fun();    输出是undefined

三、易错练习

var a = 123;
function fun()
{
    alert(a);
    var a = 456;
}

fun();

 警示框会显示undefined

因为函数里面var的a会声明提前,但是alert语句在var语句之前,此时a处于undefined状态

《关于为什么不向上找a》因为这是在函数里面新var的a,如果函数里面是"a = 456"语句才会向上找a


var a = 123;
function fun(a)
{
    alert(a);    undefined 函数调用没有实参
    a = 456;
}

fun();
alert(a);    123 全局变量,跟函数的形参没关系

05-this 

解析器在调用函数时每次都会向函数内部传递进一个隐含的参数,即this

this指向的是一个我们称为函数执行的上下文对象

根据函数的调用方式不同,this会指向不同的对象

function fun()
{
    console.log(this);
}

fun();

以函数的形式调用,this永远都是window 

var obj = {
    name:"孙悟空",
    sayName:fun
};

obj.sayName();

//obj.sayName == fun

以方法的形式调用this时,this就是调用方法的那个对象 


function fun()
{
    console.log(this.name);
}


var name = "全局的name属性";

fun();    会输出全局变量的name属性

因为以函数调用this是window

06-对象  

分类 

  1. 内建对象
  2. 宿主对象
  3. 自定义对象 

一、使用工厂方法创建对象  

通过该方法可以大批量的创建对象

function createPerson(name,age,gender)
{
    var obj = new object();
    obj.name = name;
    obj.age = age;
    obj.gender = gender;
    obj.sayName = function(){
        alert(this.name);
};
    return obj;
}

var obj1 = createPerson("白骨精",17,"男");

弊端:使用工厂方法创建的对象使用的构造函数都是object,所以创建的对象都是object这个类型,就导致我们无法区分出多种不同类型的对象 

于是就引入了构造函数

二、构造函数创建对象 

构造函数就是一个普通的函数,创建方式和普通函数没有区别

不同的是构造函数习惯上首字母大写 

构造函数和普通函数的区别就是调用的方式不同

构造函数普通函数
使用new直接调用

 构造函数的执行流程:

  1. 立即创建一个新的对象
  2. 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
  3. 逐行执行函数中的代码
  4. 将新建的对象作为返回值返回 
function Person (name,aage,gender)
{
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = function (){
        alert(this.name);
    };
}

var per = new Person();

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类

        我们将通过一个构造函数创建的对象,称为是该类的实例 


《instanceof》

检查一个对象是否是一个类的实例

console.log ( 对象 instanceof 构造函数);        返回true or false 

!!所有对象都是object的后代,所以任何对象和object做instanceof检查时都会返回true 

使所有对象共享一个方法

 我们的方法是在构造函数内部创建的,也就是构造函数每执行一次就会创建一个新的sayName方法,这就导致了构造函数执行一次就会创建一个新的方法

将sayName方法在全局作用域中定义

function Person(name,age,gender)
{
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = fun;
}

//好奇怪,类的最后居然不用打分号

function fun(){
    alert(this.name);
};

 将函数定义在全局作用域内,污染了全局作用域的命名空间,而且定义在全局作用域中也很不安全

Person.prototype.sayName = function (){
    alert(this.name);
};

 三、原型对象prototype

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype

这个属性对应着一个对象,这个对象就是原型对象 

如果函数作为普通函数调用,prototype没有任何作用

当函数以构造函数的形式调用时,它创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象 

我们可以通过__proto__来访问属性 

function MyClass()
{
    
}

var mc = new MyClass();

console.log(mc.__proto__ == MyClass.__proto__);    true

原型对象就相当于一个公共区域,所有同一个类的实例都可以访问到这个原型对象 

 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用

        如果没有则会去原型对象中寻找,如果找到则直接使用

可以很好的解决污染全局作用域命名空间的问题

以后我们创建构造函数时,可以将这些对象共有的属性和方法写到原型对象中


function MyClass(){

}

MyClass.prototype.name = "我是原型中的函数";

var mc = new MyClass();

console.log("name" in mc);

 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性

console.log(mc.hasOwnProperty("name"));

 这个方法在原型里,使用该方法只有对象自身中含有属性时,才会返回true

console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));    false

 原型对象也是对象,所以它也有原型

给我们使用一个对象的属性或方法时,会先在自身中寻找

        自身中如果有,则直接使用

        如果没有则去原型对象中寻找,如果原型对象中有,则使用

        如果没有则去原型的原型中寻找,直到找到object对象的原型

        object的原型没有原型,如果在object中依然没有找到,则返回undefined

console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));    true

四、打印对象toString() 

 当我们直接在页面中打印一个对象时,实际上是输出对象的toString()方法的返回值

如果我们希望在输出对象时不输出[object object],可以为对象添加一个toString方法 

        因为它自身有了就不会往原型上找了

五、具体对象 

1.Date

在JS中使用date对象来表示一个时间

如果直接使用构造函数创建一个date对象,则会封装成当前代码执行时间

创建一个指定的时间对象,需要在构造函数中传递一个表示时间的字符串作为参数

日期的格式 月份/日/年 时:分:妙

var d = new Date("12/03/2011 11:10:30");

方法

getDate获取当前日期对象是几日
getDay返回当前日期对象是星期几(0~6)
getMonth获取当前日期对象的月份(0~11)
0表示1月,1表示2月,2表示3月.....
getFullYear获取年份
......
getTime

获取当前日期对象的时间戳

计算机底层在保存时间时都是使用时间戳

从格林威治标准时间的1970年1月1日0时0分0秒

到当前日期所花费的毫秒数(1秒 = 1000毫秒)

now获取当前的时间戳

作用:利用时间戳来测试代码的执行性能 

 2.Math

Math和其他对象不同,它不是一个构造函数

它属于一个工具类不用创建对象,它里面封装了数学运算相关的属性和方法 

方法 

abs()绝对值
ceil()上取整
floor()下取整
round()四舍五入
random()生成一个0~1之间的随机数,左闭右开
Math.round(Math.random()*x)生成一个0~x之间的随机数
Math.round(Math.random()*(y-x)+x) 生成一个x~y之间的随机数
max()        min()
pow(x,y)x的y次幂
sqrt()开方
3.String 
charAt返回字符串中指定位置的字符
charCodeAt返回指定位置字符的字符编码
fromCharCode可以根据字符编码去获取字符
concat连接字符串
indexOf

检索字符串是否含有指定内容

如果含有,返回第一次出现的索引

如果没有,返回-1

str.indexOf("h",1)可以指定第二个参数,指定开始查找的位置
lastIndexOf从后往前检索指定内容
slice

可以从字符串中截取指定的内容

不会影响原字符串,而是将截取到的内容返回

传负值则表示从倒数第几个开始

substring

截取一段字符串,左闭右开

不同的是该方法不能传负值,如果传递了一个负值

默认为使用0,而且会自动调整参数的位置

if第二个参数小于第一个,自动交换

substr

截取字符串,对原字符串没影响

参数1:截取开始位置

参数2:截取的长度

split

可以将一个字符串拆分成一个数组

需要一个字符串作为参数,将会根据该字符串去拆分数组

toUpperCase

将一个字符串转换成大写并返回

toLowerCase

将一个字符串转换成小写并返回

07-垃圾回收(GC) 

当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象

        此时这种对象就是一个垃圾

这种对象过多会占用大量的内存空间,导致程序运行变慢

var obj = new object();
obj = null;

 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁

        我们不需要也不能进行垃圾回收的操作

我们需要做的只是将不再使用的对象设置成null即可

08-数组 

数组也是一个对象,它和普通对象功能类似,也是用来存储一些值的

不同的是普通对象是使用字符串作为属性名的,而数组是使用数字来作为索引操作元素的

var arr = new array();

//返回数组中元素的数目    前提要是连续的数组
console.log(arr.length);

对于非连续的数组,使用length会获取到数组的最大的索引+1

修改length
arr.length = 3;
//如果修改的length大于原长度,则多出部分会空出来
//如果修改的length小于原长度,则多出的元素会被删除

 向数组的最后一个位置添加元素

arr[arr.length] = 66;

数组中的元素可以是任意的数据类型,可以是对象,也可以是函数,还可以是数组

一、创建数组 

1.使用字面量来创建数组 
var arr = [1,2,3,4,5,10];

 使用字面量创建数组时,可以在创建时就指定数组中的元素 

2.使用构造函数创建数组 
var arr = new Array(10,20,12);

区别于
arr1 = new Array(10);
这是生成一个长度为10的数组

二、数组的方法 

1.push  

可以向数组的末尾添加一个或多个元素,并返回数组的新的长度 

2.pop 

删除数组的最后一个元素,并将删除的元素作为返回值返回 

3.unshift

向数组开头添加一个或多个元素,并返回新的数组长度 

4.shift

删除数组第一个元素,并将删除的元素作为返回值返回 

5.slice

从某个已有的数组返回选定的元素

该方法不会改变原数组,而是将截取到的元素封装到一个新数组中返回

var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];

arr.slice(0,2);    只取了前两个

//arr.slice(start,end);    前者是必需的

索引可以传递一个负值,-1表示倒数第一个,-2表示倒数第二个 

6.splice 

删除元素,并向数组添加新元素

使用该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回

参数:

  1. 第一个,表示开始的位置的索引
  2. 第二个,表示删除的数量  
  3. 第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置之前
 7.其他方法
concat连接两个或更多的数组,并返回结果
该方法不会对原数组产生影响
join

把数组的所有元素放入一个字符串中

元素通过指定的分隔符进行分割

该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回

join()可以指定一个字符串作为参数成为数组中元素的连接符

如果不指定,默认使用,

reverse颠倒数组中元素的顺序,该方法会直接修改原数组
sort对数组中的元素排序,也会直接修改原数组

> 0 交换位置

< 0 不变

= 0 不变

该方法按照Unicode编码排序,所以即使对数字排序时,可能会得到错误的结果

我们可以自己来指定排序的规则

        我们可以在sort()添加一个回调函数,来指定排序规则......

升序 a - b         降序 b - a

三、遍历 

 筛选出年龄大于18的对象

function Person(name,age){
    this.name = name;
    this.age = age;
}

var per = new Person("孙悟空",18);
var per2 = new Person("猪八戒",28);
var per3 = new Person("红孩儿",8);
var per4 = new Person("蜘蛛精",16);
var per5 = new Person("二郎神",38);

var perarr = [per,per2,per3,per4,per5];

function getAdult(arr){
    var newArr = [];

    for(var i = 0; i < arr.length; i++)
    {
        if(arr[i].age >= 18) newArr.push(arr[i]);
    }

    return newArr;
}

var result = getAdult(perarr);
 forEach( )

这个方法只支持IE8以上的浏览器

该方法需要一个函数作为参数

var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];

arr.forEach(function(){
    console.log("hello");
});

 像这种函数,由我们创建但是不由我们调用的,我们称为回调函数

数组中有几个元素就会执行几次,每次执行时浏览器会将遍历到的元素以实参的形式传递进来,我们可以定义形参,来读取这些内容

var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];

arr.forEach(function(a){
    console.log("a = "+a);
});


a = 孙悟空;
a = 猪八戒;
a = 沙和尚;
a = 唐僧;
a = 白骨精;

 浏览器会在回调函数中传递三个参数

  1. 第一个参数,是当前正在遍历的元素value
  2. 第二个参数,是当前正在遍历的元素的索引index
  3. 第三个参数,是当前正在遍历的数组

四、数组去重练习 

var arr = [1,2,3,2,1,3,4,2,5];

for(var i = 0; i < arr.length; i++){
    for(var j = i+1; j < arr.length; j++){
        if(arr[j] == arr[i]) arr.splice(j,1);
//当删除了当前j所在的元素以后,后边的元素会自动补位
//此时将不会再比较这个元素,我们需要再比较一次j所在的位置的元素
        j--;    再比一遍
    }
}

09-包装类 

基本数据类型: String Number Boolean Null Undefined

引用数据类型: Object

在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象

        String()        可以将基本数据类型的字符串转换为String对象

        Number()        可以将基本数据类型的数字转换为Number对象

        Boolean()         可以将基本数据类型的布尔值转换为Boolean对象

var b = new Boolean(false);

b转换成了一个对象,对象转换成布尔值都是true

if(b){
    alert("我运行了~");
}

 但是!!注意:我们在实际应用中不会使用基本数据类型的对象

        如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果

方法和属性只能添加给对象,不能添加给基本数据类型

当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后再调用对象的属性和方法 

10-正则表达式 

用于定义一些字符串的规则

计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来 

一、创建正则表达式的对象

1、使用构造函数创建

语法:var 变量  =  new  RegExp ( " 正则表达式 " , " 匹配模式 “ );

使用typeof检查正则对象,会返回object

2、使用字面量创建 

语法:var  变量  =   / 正则表达式 / 匹配模式 

var reg = RegExp("a","i");
var reg = /a/i;

二、正则表达式的方法

test() 使用这个方法可以用来检查一个字符串是否符合正则表达式的规则

严格区分大小写的哦!!!

符合则true,反则false 

var reg = new RegExp("a");
这个正则表达式可以用来检查一个字符串中是否含有a
严格区分大小写

var str = "a";

var result = reg.test(str);
console.log(result);

 三、匹配模式

在构造函数中可以传递一个匹配模式作为第二个参数

可以是

        i 忽略大小写

        g 全局匹配模式  

二、语法

 1、创建一个正则表达式,检查一个字符串中是否有a或b或c

reg = / a | b | c /;
console.log(reg.test("zyn"));

 2、创建一个正则表达式,检查一个字符串中是否有字母

reg = /[abcdefghijklmnopqrstuvwxyz]/;

 [ ] 也表示"或"

但是还可以更简便

reg = /[a-z]/;    表示任意小写字母
/[A-Z]/            表示任意大写字母
/[A-z]/            表示任意字母

 3、检查一个字符串中是否含有abc或adc或aec

reg = /abc/adc/aec/;
reg = /a[bde]c/;

 这个或的用法还蛮妙的

 4、除了

reg = /[^ab]/;    找除了ab以外的东西
console.log(reg.test("abc"));

只要找到除了ab以外的东西就可以,所以上行代码返回true 

5、量词

创建一个正则表达式检查一个字符串是否含有aaa 

var reg = /aaa/;
console.log(reg.test("abc"));

 通过量词可以设置一个内容出现的次数

量词只对它前边的一个内容起作用,可以使用括号括起来

var reg = /a{3}/;

我希望搜索一个b,它出现1~3次,且前后分别挨着一个a和一个c

var reg = /ab{1,3}c/;
var reg = /ab{3,}c/;    3次及以上

 + 至少一个,相当于{1,}

* 0个或多个,相当于{0,}

?0个或1个,相当于{0,1}

^ 表示开头       reg = /^a/;

$表示结尾        reg = /a&/;

        如果在正则表达式中同时使用^ $,则要求字符串必须完全符合正则表达式

        例如  reg = /^a$/;        字符串中就只能包含一个a 


 手机号的正则 

创建一个正则表达式,用来检查一个字符串是否是一个合法的手机号

手机号的规则:

  1. 以1开头^1
  2. 第二位是 3 - 9的任意数字[3-9]
  3. 三位及以后是9个的任意数字[0-9]{9}$ 
var phonestr = "13688379394";
var phonereg = /^1[3-9][0-9]{9}$/;
console.log(phonereg.test(phonestr));

. 表示任意字符

在正则表达式中使用\作为转义字符        \.表示单纯的.

使用构造函数时,由于它的参数是一个字符串,而 \ 是字符串中的转义字符

如果要使用\则需要用 \ \ 来代替

如果是在字面量里面则 \ 就够了 

var reg = /\./;
reg = new RegExp("\\.");

\w   任意字母、数字、_ 

\W    除了字母、数字、_

\d    任意数字

\D    除了数字

\s    空格

\S    除了空格

\b    单词边界

\B    除了单词边界


创建一个正则表达式检查一个字符串是否含有单词child 

var reg = /\bchild\b/;
console.log(reg.test("hello children"));

 去除空格就是使用""来替换空格

str = str.replace(/\s/g,"");
str = str.replace(/^\s*/,"");    去除开头的空格
str = str.replace(/\s*$/,"");    去掉结尾的空格
 邮件的正则

hello.nihao@abc.com.cn 

 任意字母数字_                \w{3,}

(.任意字母数字_)@        (\.\w+)*        *的作用就是让括号里的东西可有可无

任意字母数字                         [A-z0-9]+

. 任意字母(2-5位)                \.[A-z]{2,5}

. 任意字母(2-5位)                (\.[A-z]{2,5})*             

或者直接将最后两行合并成        (\.[A-z]{2,5}){1,2} 

注意别忘了开头和结尾的限制!!!

三、字符串和正则相关的方法

1. split( )

可以将一个字符串拆分成一个数组

var str = "1a2b3c4d5e6f";
var result = str.split("c");
拆分之后,c会没掉
console.log(result.length); 2  

这个方法即使不指定全局匹配,也会全都拆分 

2. search()

可以搜索字符串中是否含有指定内容

search只会查找第一个,即使设置全局匹配也没用

str = "hello abc";
result = str.search("abc");
consolr.log(result);    6

如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1

 它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串

搜索字符串中是否含有abc/aec/afc

result = str.search(/a[bef]c/);
 3. match

可以根据正则表达式,从一个字符串中将符合条件的内容提取出来

match会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果

var str = "1a2b3c4d5e6f7";
var result = str.match(/[A-z]/);
console.log(result);    a 

 默认情况下我们的match只会找到第一个符合要求的内容,找到以后就停止检索

我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容

result = str.match(/[a-z]/ig);
既是全局匹配模式又忽略大小写

 可以为一个正则表达式设置多个匹配模式,且顺序无所谓

4. replace

可以将字符串中指定内容替换为新的内容

参数:

        (正则表达式)1.被替换的内容       2.新的内容

默认只会替换第一个,可以接受正则表达式作为参数

如果想做删除操作,可以将新的内容设置成空串

        

11-宿主对象 

有浏览器或者运行环境提供的对象 

一、DOM 

 浏览器已经为我们提供了文档节点对象,这个对象是window属性

可以在页面中直接使用,文档节点代表的是整个网页

<button id="btn">我是一个按钮</button>
<script type = "text/javascript">
    
    获取到button对象
    var btn = document.getElementById("btn");

    修改按钮的文字
    btn.innerHTML = "I'm a button";

</script>

 事件

我们可以在事件对应的属性中设置一些js代码

这样当事件被触发时,这些代码将会执行

这种写法我们称为结构和行为耦合,不方便维护,不推荐使用 

可以为按钮的对应事件绑定处理函数的形式来响应事件 

<button id="btn">我是一个按钮</button>
<script type = "text/javascript">
    
    获取到button对象
    var btn = document.getElementById("btn");

    绑定一个单击事件
    btn.onclick = function(){
        alert("别点了");

    }
</script>

像这种为单击事件绑定的函数,我们称为单机响应函数

一、事件对象

当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数

在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标 键盘哪个按键被按下 鼠标滚轮滚动的方向

在IE8中,响应函数被触发时,浏览器不会传递事件对象

在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的

因此,这里又牵扯到兼容性的调整。大概意思就是先根据事件对象存不存在来判断是什么浏览器

如果事件对象不存在就在前面加一个 window.(这里详细见尚硅谷p111,我偷个懒)

event = event || window.event;
另一个很优雅的写法

div跟随鼠标移动练习

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>div跟随鼠标移动</title>
    <style>
        #box1 {
            width: 50px;
            height: 50px;
            background-color: pink;
            position: absolute;
            /* 一定要在有定位的前提下才能修改偏移量!!! */
        }
    </style>
    <script>
        window.onload = function () {
            var box1 = document.getElementById("box1");

            //在整个页面下触发鼠标移动事件
            document.onmousemove = function (event) {

                //解决兼容性问题
                event = event || window.event;

                //获取到鼠标的坐标

                var left = event.clientX;
                var top = event.clientY;

                box1.style.left = left + "px";
                box1.style.top = top + "px";
            }
        }
    </script>
</head>

<body>
    <div id="box1"></div>
</body>

</html>

 clientX 和 clientY用于获取鼠标在当前的可见窗口的坐标

但是div的偏移量是相对于整个页面的

于是,我们选择采用pageX和pageY,可以获取鼠标对于当前页面的坐标

<script>
        window.onload = function () {
            var box1 = document.getElementById("box1");

            //在整个页面下触发鼠标移动事件
            document.onmousemove = function (event) {

                //解决兼容性问题
                event = event || window.event;

                //获取到鼠标的坐标

                var left = event.pageX;
                var top = event.pageY;

                box1.style.left = left + "px";
                box1.style.top = top + "px";
            }
        }
    </script>

 但是这两个属性在IE8中不支持,所以如果需要兼容IE8,则不要使用

根据观察我们发现当下拉滚动条时,鼠标与div相离的距离正是下拉滚动条的长度,即scrollTop

获取滚动条滚动的距离

Chrome认为浏览器的滚动条是body的,可以通过body.scrollTop来获取
var st = document.body.scrollTop;


火狐等浏览器认为浏览器的滚动条是HTML的,即body的父元素的
var st = document.documentElement.scrollTop;

为了处理兼容性问题
var st = document.body.scrollTop || document.documentElement.scrollTop;

 之后在div的垂直偏移量加上st就好了 


  

二、事件的冒泡

 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发

 在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡

event.cancelBubble = true;

三、事件的委派 

指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件

(我们希望,只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的 

我们可以尝试将其绑定为元素的共同的祖先元素) 

 事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能

为了保证触发区域的准确性,如果触发事件的对象是我们期望的元素则执行,否则不执行 

用event.target获取我们当前触发的对象
然后再写一个if判断

 四、事件的绑定

 使用 对象.事件 = 函数 的形式绑定响应函数

它只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个

如果绑定了多个,则后边会覆盖掉前边的

 addEventListener()

通过这个方法也可以为元素绑定响应函数

参数:

        1. 事件的字符串,不要on

        2. 回调函数,当事件触发时该函数会被调用

        3. 是否在捕获阶段触发事件,需要一个布尔值,一般都传false

使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数

这样当事件被触发时,响应函数将会按照函数的绑定顺序执行

不支持IE8及以下的浏览器

attachEvent()

支持IE8及以下的浏览器

参数

        1. 事件的字符串,要on

        2. 回调函数

这个方法也可以同时为一个事件绑定多个处理函数

        不同的是它是后绑定先执行,执行顺序和addEventListener()相反 

处理兼容性 

定义一个函数,用来为指定元素绑定响应函数

addEventListener()中的this,是绑定事件的对象

attachEvent()中的this,是window

我们需要统一两个方法中的this

this是谁是由调用方式决定的,于是我们要将调用函数的权利拿回来,在匿名函数中调用回调函数

参数:

        2. obj 要绑定事件的对象

        3. eventStr 事件的字符串(不要on,因为加上比去掉容易)

        4. callback 回调函数 

function bind(obj,eventStr,callback){

    if(obj.addEventListener){
        obj.addEventListener(eventStr,callback,false);
    }
    else{
        obj.attachEvent("on" + eventStr,function(){
            
            callback.call(obj);
        })     
    }
}

五、事件的传播 

 关于事件的传播网景公司和微软公司有不同的理解

微软公司认为事件应该是由内向外传播的,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行

网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后再向内传播给后代元素

W3C综合了两个公司的方案,将事件传播分成了三个阶段:

  1. 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
  2. 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件
  3. 冒泡阶段:事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件 

如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true

一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false 


文档的加载 

  1. 将js代码编写到页面的下部就是为了,可以在页面加载完毕以后再执行代码
  2. 也可以等整个页面加载完成之后,这也算是一个事件,我们就可以为这个事件套一个函数,然后把要执行的代码都写进函数 
window.onload = function(){
    
};

DOM查询 

  • getElementsByTagName 这个方法会给我们返回一个类数组对象,所有查询到的元素都会封装到对象中
  • innnerHTML用于获取元素内部的HTML代码,对于自结束标签,这个属性没有意义

        例如表单input是自结束标签,并没有内部,所以无法获取 

  • 如果需要读取元素节点属性,直接使用元素.属性名 

        注意:class属性不能采用这种方式,因为class是js的保留字

        读取class属性时需要用.classname 

 

childNodes,firstchild属性会获取包括文本节点,元素节点在内的所有节点

根据DOM标签标签,标签间空白也会当成文本节点

        注意:在IE8及以下的浏览器中,不会将空白文本当成子节点

children属性可以获取当前元素的所有子元素 (即不包括空白)

firstElementChild获取当前元素的第一个子元素 

        firstElementChild不支持IE8及以下的浏览器,如果需要兼容他们尽量不要使用 

剩余方法

1.获取body标签 

1.    var body = document.getElementById("body")[0];

在document中有一个属性body,他保存的是body的引用

2.    var body = document.body;

 2.获取html根标签

var html = document.documentElement;

3.document.all代表的是页面中所有的元素,虽然显示出来是undefined 

var all = document.documentElement;
var all = document.getElementByTagName("*");

以上两种方法效果一样

 4. getElementsByClassName不支持IE8及以下的浏览器

5. documentquerySelector()

        需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象

eg.  获取class为box1中的所有div

        先提取box1,在提取div分两步有点麻烦,现在可以一步到位

var div = document.querySelector(".box div");

IE8中虽然没有getElemrntsByClassName,可以用querySelector代替

使用该方法总会返回唯一的一个元素,如果满足条件的元素有多个,那么它只会返回第一个 !!

6. querySelectorAll 

该方法和querySelector()用法类似,不同的是它会将符合条件的元素封装到一个数组里面

即使符合条件的元素只有一个,它也会返回数组 

DOM增删改 

1. createElement

用于创建一个元素节点对象

它需要一个标签名作为参数,将会根据该标签名创建元素节点对象,并将创建好的对象作为返回值返回

var li = document.createElement("li");

2.createTextNode

用来创建一个文本节点对象

3.appendChild

向一个父节点中添加子节点

用法:父节点.appendChild(子节点)

4.insertBefore

在指定的子节点前面插入新的子节点

用法:使用父节点调用

fatherNode.insertBefore(newChild: Node, refChild: Node);

5.replaceChild

替换子节点 ,仍然是父节点调用

fatherNode.insertBefore(newChild: Node, oldChild: Node);

 6.removeChild

删除子节点,父节点调用

常见搭配parentNode食用

 最新超简便写法

var city = document.getElementById("city");
city.innerHTML = "<li>广州</li>";

但不推荐完全使用以上方法,推荐两种方法结合使用

var city = document.getElementById("city");
city.innerHTML = "广州";
city.appendChild(li);

修改CSS

一、通过JS修改元素的样式 

语法:元素.style.样式名 = 样式值 

注意:如果CSS的样式名中含有 - ,需要改成驼峰命名法

        例如backgroundColor

通过style属性设置的样式都是内联样式,而内联样式有较高的优先级,所以通过js修改的样式往往会立即显示(除了如果在样式中写了 ! important...

 通过style属性设置和读取的都是内联样式,无法读取样式表中的样式

二、获取元素当前显示的元素 (以下两种方法获取到的样式都是只读的,不能修改,如果要修改必须通过Style属性)

1. currentStyle

语法:元素.currentStyle.样式名 

如果当前元素没有设置该样式,则获取它的默认值

但素,currentStyle只有IE浏览器支持,其他浏览器不支持 

2. getComputedStyle 

这个方法是window的方法,可以直接使用 

需要两个参数:

  1. 第一个:要获取的元素
  2. 第二个 :可以传递一个伪元素(即before,after...),一般都传null

该方法会返回一个对象,对象中封装了当前元素对应的样式 

可以通过对象.样式名来读取样式

var obj = getComputedStyle(box1,null);
alert(obj.width);

 如果获取的样式没有设置,则会获取到真实的值,而不是默认值

比如:没有设置width,它不会获取到auto,而是一个长度

不支持IE8即以下的浏览器


处理兼容性问题 

既能兼容正常浏览器,又能兼容IE8及以下的浏览器 

定义一个函数,用来获取指定元素的当前的样式

参数:

                obj  要获取的元素

                name 要获取的样式名

function getStyle(obj,name){
    
    if(window.getComputedStyle){
        return getComputedStyle(obj,null)[name];    正常浏览器的方法
        这里不能用.name
    }
    else return obj.currentStyle[name];
}

如果没有加window,getComputedStyle是一个变量,是需要去作用域中寻找的,变量没找到时会报错的。加了一个window之后,getComputedStyle变成了一个对象的属性,但是属性没找到只会返回undefined

其他样式的相关属性 

这些属性都是不带px的,返回都是一个数字,可以直接进行计算

这些属性都是只读不改的

1. clientWidth 和 clientHeight

获取的是可见宽度和可见高度 ,即元素的高度和宽度,包括内容区和内边距

2. offsetWidth 和 offsetHeight

获取元素整个的高度和宽度,包括内容区,内边距和边框 

3.offsetParent

可以获取当前元素的定位父元素

会获取到里当前元素最近的开启了定位的祖先元素,如果所有的祖先元素都没有开启定位则返回body

4.offsetLeft 和 offsetTop

获取当前元素相对于其定位父元素的水平偏移量和垂直偏移量 

5. scrollHeight 和 scrollWidth

可以获取元素整个滚动区域的高度和宽度

6.scrollLeft 和 scrollTop

获取水平滚动条滚动的距离 

 当满足scrollHeight - scrollTop == clientHeight,说明垂直滚动条滚动到底了

(用于让用户必须阅读协议到底才能进行下一步时使用)

二、BOM 

1.定时器 

如果希望一段程序,可以每间隔一段时间执行一次,可以使用定时调用 

setInterval() 

定时调用,可以将一个函数,每隔一段时间执行一次

参数:

        1.回调函数,该函数会每隔一段时间被调用一次

        2.每次调用间隔的时间,单位是毫秒 

返回值:

        返回一个Number类型的数据 ,这个数字用来作为定时器的唯一标识

var timer = setInterval(function(){

},1000);

console.log(timer);

clearInterval(timer);

clearInterval ( ) 可以用来关闭一个定时器

方法中需要一个定时器的标识作为参数,这样将关闭标识对应的定时器 

切换图片练习 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>切换图片</title>
    <script>

        window.onload = function () {

            var timer;

            var btn = document.getElementById("btn");
            //我们每点击一次按钮,就会开启一个定时器
            //点击多次就会开启多个定时器,这就导致图片的切换速度过快
            btn.onclick = function () {

                var img = document.getElementById("img");

                var imgArr = ["images/傻妞.jpg", "images/吹蜡烛.jpg", "images/开心跳起来.jpg"];
                var index = 0;

                var btn = document.getElementById("btn");

                //在开启定时器之前,需要将当前元素上的其他定时器关闭
                clearInterval(timer);

                timer = setInterval(function () {
                    index++;

                    if (index >= imgArr.length) index = 0;

                    img.src = imgArr[index];
                }, 1000);
            }

            var btn02 = document.getElementById("btn02");

            btn02.onclick = function () {
                clearInterval(timer);
            }

        }
    </script>
</head>

<body>
    <img src="images/傻妞.jpg" id="img">
    <button id="btn">开始</button>
    <button id="btn02">停止</button>
</body>

</html>

 2. 延时调用

延时调用一个函数不马上执行,而是隔一段时间以后再执行,而且只会执行一次。 

延时调用和定时调用的区别,定时调用会执行多次,而延时调用只会执行一次 

延时调用和定时调用实际上是可以互相代替的,在开发中可以根据自己的需要去选择 

var timer = setTimeout(function(){
    
},3000);

使用clearTimeout来关闭一个延时调用
clearTimeout(timer);

 12-JSON

js中的对象只有js自己认识,其他的语言都不认识

JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别

        并且可以转换为任意语言中的对象,JSON在开发中主要用来数据的交互

Javascript   object   Notation   JS对象表示法 

JSON和JS对象的格式一样,只不过JSON字符串中的属性名必须加双引号,其他的和JS语法一致

JSON分类:

  1. 对象{}
  2. 数组[]
var obj = '{"name":"孙悟空","age":"18","gender":"男"}';

var arr = '[1,2,3,"hello",true]';

JSON中允许的值:

  1. 字符串
  2. 数值
  3. 布尔值
  4. null
  5. 对象
  6. 数组

一、将JSON字符串转换为JS中的对象 

在JS中,为我们提供一个工具类,就叫JSON

这个对象可以帮助我们将一个JSON转换为JS对象,也可以将一个JS对象转换为JSON      

JSON.parse (  )          json  ——>  js对象

        需要一个JSON字符串作为参数,会将该字符串转换为JS对象

JSON. stringify ( )         js对象 ——> json 

        需要一个js对象作为参数,会返回一个JSON字符串 

JSON不支持IE7及以下的浏览器

eval ( )

        这个函数可以用来执行一段字符串形式的代码,并将执行结果返回

        如果使用eval()执行的字符串中含有{},它会将{}当成是代码块 

                如果不希望将其当成代码块解析,则需要在字符串前后各加一个()

var str = "alert('hello');";

eval("("+str+")");

 但是在开发中尽量不要使用,首先它的执行性能比较差,然后它还具有安全隐患

如果需要兼容IE7及以下的JSON操作,则可以通过引入一个外部的js文件来处理 

13-类

我们可以通过通过修改元素的class属性来间接的修改样式

这样一来,我们只需要修改一次,即可同时修改多个样式

浏览器只需要重新渲染页面一次,性能比较好

并且这种方式,可以使表现和行为进一步的分离 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>类</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .b1 {
            width: 100px;
            height: 100px;
            background-color: pink;
        }

        .b2 {
            height: 200px;
            background-color: powderblue;
        }
    </style>
    <script>
        window.onload = function () {
            var btn01 = document.getElementById("btn01");

            btn01.onclick = function () {
                var box = document.getElementById("box");
                box.className += " b2";
            }
        }

    </script>
</head>

<body>
    <button id="btn01">点击按钮修改box的样式</button>
    <br /><br />
    <div id="box" class="b1"></div>
</body>

</html>

定义一个函数,用来向一个元素添加指定的class属性值

参数:

        obj 要添加的class属性的元素

        cn 要添加的class值

function addClass(obj, cn) {
    obj.className += "" + cn;
}

 但是这样会使得按一下按钮就添加一个class,所以我们引入hasClass来判断该元素有没有这个class属性

//判断obj中有没有cn class
function hasClass(obj, cn) {
    var reg = /\bb2\b/;
    \b是单词边界
    return reg.test(obj.className);
}

但是这样就把判断写死了,我们要让正则表达式里的是cn

即动态创造正则表达式

function hasClass(obj,cn){
    
    var reg = new RegExp("\\b"+cn+"\\b");

    return reg.test(obj.className);
}

 3. 删除class的函数

将要删除的class用空串代替

function removeClass(obj,cn){
    var reg = new RegExp("\\b"+cn+"\\b");

    obj.className = obj.className.replace(reg,"");
}

4. 切换类函数

有则删除,没有则添加 

function toggleClass(obj,cn){
    
    if(hasClass(obj,cn)){
        removeClass(obj,cn);
    }
    else addClass(obj,cn);
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值