● 为什么要学习JAVAscript
○ 前端核心语言
○ 框架基础
● JAVAscript能做什么
○ 浏览器端
○ node(服务器端)
● JAVAScript的历史
○ Nombas 的 ScriptEase
大概在 1992 年,一家称作 Nombas 的公司开发了一种叫做 C 减减(C-minus-minus,简称 Cmm)的嵌入式脚本语言。
○ Netscape 发明了 JavaScript
1995 年发行的 Netscape Navigator 2.0 开发一个称之为 LiveScript 的脚本语言,当时的目的是在浏览器和服务器(本来要叫它 LiveWire)端使用它。
○ 微软的JSript
微软也决定进军浏览器,发布了 IE 3.0 并搭载了一个 JavaScript 的克隆版,叫做 JScript(这样命名是为了避免与 Netscape 潜在的许可纠纷)。
○ 标准化
1997 年,JavaScript 1.1 作为一个草案提交给欧洲计算机制造商协会(ECMA)。
版本为:ECMA-262 维护组织为:TC-39
○ ECMAScript和javaScript
当时有java语言了,又想强调这个东西是让ECMA规则,所以就这样一个神奇的东西诞生了,这个东西的名称就叫做ECMAScript。但注意他是一个标准
所以我们讲javaScript是ECMAScript的实现,ECMAscript是javaScript的标准
○ javaScript的组成
javaScript = ECMAScript + DOM + BOM
○ 关于ES6
后面我们会学习ES6,当然这里指的是版本,我们可以说现在学习的是ES5,我们把ES2015之前的版本,统称为ES5。也就是现在我们即将学习的版本拉
○ JS社区
因为JS语言存在很多局限性,比如没有模块化,变量没有类型,没有块级作用域等。所以在JS社区中,存在一些其他的版本,比如:sea.js、require.js等
● JS的工作原理
在初期,JS的设定初衷就是在浏览器上运行的脚本语言(操作dom),所以避免不了需要采用单线程的模式。如果采用多线程,那么就会出现多个线程同时对同一个dom进行修改的情况,这样太复杂了。
那么采用单线程,也就是说执行代码的线程的只有一个!但是也会出现一个问题,也就是如果任务比较多的情况下,任务是需要排队的。那么就会导致如果有一个任务比较耗时,我们会了解决这个问题,可以采用异步的方式去解决。
同步模式
异步模式
● JS垃圾回收机制
JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
JS执行环境中的垃圾回收器怎样才能检测哪块内存可以被回收有两种方式:标记清除(mark and sweep)、引用计数(reference counting)。
简单来说,有2种情况,会被视为垃圾。
● 没有被引用的对象
● 几个对象相互引用形成闭环
那什么情况不会被视为垃圾呢?
● 全局变量(时刻待命)
● 有具体引用关系的对象(闭包)
function test(){
var a=1;
return function(){
console.log(++a);
}
}
test();
test();
● JS代码如何进行延迟加载
js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
我了解到的几种方式是:
1、将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
2、给 js 脚本添加 defer 属性,有derer的话,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成,并且多个defer会按照顺序进行加载。
3、给 js 脚本添加 async属性,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)
4、动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
defer和async的区别
简单来说:区别主要在于一个执行时间,defer会在文档解析完之后执行,并且多个defer会按照顺序执行,而async则是在js加载好之后就会执行,并且多个async,哪个加载好就执行哪个
● 变量的作用域
全局变量和局部变量。javascript的作用域是相对函数而言的,可以称为函数作用域
全局作用域:
最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的
局部作用域:
局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的
● 变量提升
当我们使用未定义的变量时,存在变量提升的情况。
1.所有的声明都会提升到作用域的最顶上去。
2.同一个变量只会声明一次,其他的会被忽略掉或者覆盖掉。
3.函数声明的优先级高于变量声明的优先级,并且函数声明和函数定义的部分一起被提升。
● JavaScript一共有8种数据类型
8种基本数据类型:
Undefined、Null、Boolean、Number、String、Symbol(es6新增,表示独一无二的值)和BigInt(es10新增,是指安全存储、操作大整数。但是很多人不把这个做为一个类型)
1种引用数据类型
Object(Object本质上是由一组无序的名值对组成的)。
里面包含 function、Array、Date等。
JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。
原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
● null 和 undefined 的区别
当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等 号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
● 深拷贝与浅拷贝
主要针对于引用数据类型参数说的,浅拷贝表示仅拷贝引用地址,深拷贝表示对于对象的克隆
如何实现深拷贝:
1、JSON序列化
JSON.parse(JSON.stringify(obj))
2、Object.assign()拷贝
Object.assign({
}, obj1);//深拷贝
● ++ --(面试题)最终输出a,b,c,d,e分别是多少
var a = 10;
var b = a++;
var c = ++a;
var d = --a;
var e = a–;
● 类型转换
转换成字符串
str.toString()和String()包装器
number ‘1’
null ‘null’
bool ‘true’
数组 ‘1,2,3,4,5’
对象 [object object]
转成bool类型
Boolean()
有且只有以下情况为假
“” 空的字符串
为 0 的数字
为 null 的对象
为 undefined 的对象
布尔值 false
转换成数值类型Number()
bool 1或者0
null 0
undefined NaN
12.3 12.3
{} NaN
字符串
‘123’ 123
‘小明’ NaN
‘’ 0
‘123abc’ NaN
parseInt()
转换的值是null,undefined,boolean,均转换为NaN
12.3 12
‘’ NaN
‘123abc’ 123
parfloat()
基本功能和parseInt一致,可以保留小数
● 加法 +
当m,n不为String,Object类型的时候,先将m,n转换为Number类型
当m,n有一个为String,无论另一个操作数为何(但不为对象)都要转换为String,然后再进行拼接
分数转换,把百分制转换成ABCDE <60 E 60-70 D 70-80 C 80-90 B 90 - 100 A
var a = prompt('输入分数');
if (a>=90&&a<=100) {
document.write("成绩为A");
} else if (a>=80&&a<90) {
document.write("成绩为B");
}else if (a>=70&&a<80) {
document.write("成绩为C");
}else if (a>=60&&a<70) {
document.write("成绩为D");
}else if (a>100) {
document.write("无效成绩");
}else {
document.write("成绩为E");
}
从大到小排序a、b、c三个整数
var a=2;b=3;c=4;
var n1,n2,n3;
if(a>b&&a>c){
n1 = a;
if(b>c){
n2 = b;
n3 = c;
}else{
n2 = c;
n3 = b;
}
}else{
n3 = a
if(b>c){
n1=b;
n2=c;
}else{
n1=c;
n2=b;
}
}
console.log(n1,n2,n3);
● 循环控制语句
关键字 break
如果想在所有迭代前退出,即可使用break。当执行break后,会立即跳出循环体,执行下面的代码。
关键字 continue
与break不同的是,continue不会跳出循环。而是立即结束当前循环,进入下一次循环。
Label语句
使用label语句可以在代码中添加标签,以便将来使用
入职薪水10k,每年涨幅5%,50年后工资多少?
var xinShui=10000;
for(var i=1;i<=20;i++){
xinShui+=xinShui*0.05;
}
document.write('20年后工资:'+xinShui+'
');
有1元,2元,5元的钱,现在凑成20元,有多少种可能性?
var keNengxing1=0;
for (var Yi=0;Yi<=20;Yi++) {
for (var Er=0;Er<=10;Er++) {
for (var Wu=0;Wu<=4;Wu++) {
var zongQian=Yi*1+Er*2+Wu*5;//总钱数
if (zongQian==20){
keNengxing1++;
document.write('1元:'+Yi+'张; '+'2元:'+Er+'张; '+'5元:'+Wu+'张; '+'
')
}
}
}
}
document.write('凑20元的可能性:'+keNengxing1+'
');//29
● 创建对象
var boyfriend = {
height:180,
weight:70,
money:'多',
cook(){
},
housework(){
}
}
● 如何遍历数组和对象
for - in循环:
在数组中:for(k in arr) k为数组的键,值的为arr[k]
在对象中:for(k in obj) k为对象的属性名,属性值为obj[k]
● valueOf和toString
valueOf为对象的原始值,通常由 JavaScript 在后台自动调用,并不显式地出现在代码中。
如果重写了valueOf和toString方法,在涉及到运算时,会优先调用valueOf方法,涉及到显示时,会优先调用toSting方法
如果只重写了valueOf()或者toString()方法,则调用该方法,并将返回值用Number()转换。
如果两个方法都重写了,则调用valueOf(),并将返回值用Number()转换。
var obj = {
num:1,
toString : function(){
return this.num+1;
},
valueOf : function(){
return this.num+2;
}
}
console.log(obj==2);//false
alert(obj)//2
● 对象的检测属性
in 检测某属性是否是某对象的自有属性或者是继承属性
Object.prototype.hasOwnProperty()检测给定的属性是否是对象的自有属性,对于继承属性将返回false
Object.prototype.propertyIsEnumerable() 检测给定的属性是否是该对象的自有属性,并且该属性是可枚举的。通常由JS代码创建的属性都是可枚举的,但是可以使用特殊的方法改变可枚举性。
var obj = {
name:'zhangsan'
}
console.log('toString' in obj);
console.log(obj.hasOwnProperty('toString'));
console.log(obj.propertyIsEnumerable('toString'));
● 检测对象是否为一个家族
isPrototypeOf 方法用于测试一个对象是否存在于另一个对象的原型链上。(原型指向)
instanceof 运算符用来检测一个对象是否是某个构造函数(类型)的实例(new)
function Animal(){
}
var o1 = new Animal();
function dog(){
}
dog.prototype = new Animal();
var o2= new dog();
console.log(Object.prototype.isPrototypeOf(obj));
console.log(Animal.prototype.isPrototypeOf(o2));
console.log(o1 instanceof Animal);
● 如何为对象的属性进行描述
Object.defineProperty(obj,'sex',{
configurable:false,//是否可以被删除
enumerable:false,//是否可以枚举
writable:false,//是否可以被修改
value:'男'//值
})
//查看对象的某个属性的特性
var res = Object.getOwnPropertyDescriptor(obj,'num');
//一次设置多个
var book = {
};
Object.defineProperties(book,{
_year :{
//数据属性
value:1001},
edition :{
//数据属性
value:1},
}
)
● 构造器属性
var obj = {
_num:0
}
//构造器属性:可以自定义属性的set和get方法
Object.defineProperty(obj,'num',{
set:function(num){
this._num = '数字'+num
},
get:function(){
return this._num
}
})
console.log(obj.num);
obj.num=5;
console.log(obj.num)
● 对象的序列化JSON
var json_str = ‘{“name”:“zhangsan”,“sex”:“男”}’//是一种有规则的字符串
JSON.stringify(obj) 将对象序列化为Json字符串,只能序列化对象可枚举的自有属性
JSON.parse(jsonStr) 反序列化
● prototype、proto、constructor三者的关系
1、prototype:
每一个函数都有一个prototype这个属性,而这个属性指向一个对象,这个对象我们叫做原型对象
作用:节约内存扩展属性和方法可以实现类之间的继承
2、proto:
每一个对象都有一个__proto__属性,__proto__指向创建自己的那个构造函数的原型对象对象可以直接访问__proto__里面的属性和方法
3、constructor:
指向创建自己的那个构造函数 ,是原型上的方法
总结:
当我们创建一个构造函数的时候这个构造函数自带了一个prototype属性,而这个属性指向一个对象,也就是原型对象。 这个原型对象里面有一个constructor构造器,它的作用是指向创建自己的构造函数。
除此之外 prototype还可以存放公共的属性和方法。
当我们实例化一个对象的时候(被new调用的时候),这个对象自带了一个 proto 属性,
这个proto 指向创建自己的构造函数的原型对象。可以使用这个原型对象里面的属性和方法
● 原型链 (JS原型与原型链继承)
实例对象与原型之间的连接,叫做原型链。proto( 隐式连接 )
JS在创建对象的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。
● arguments
arguments是一个类数组对象,包含着传入函数中的所有参数。
arguments对象还有一个callee的属性,用来指向拥有这个arguments对象的函数
funciton dg(num){
//递归实例
if(num<=1)
return 1
else
return num * arguments.callee(num-1)
}
● this
this指向函数据以执行的环境对象,在浏览器的函数内部this对象指向的就是window,nodejs环境this对象指向的是global。
如果函数是直接执行的,没有其他的对象进行.调用,那么this指向的是window, 如果是某个对象进行.调用的函数,this会指向那个对象(默认情况下)
● IIFE
是一种立即调用的函数表达式。因为在ES5中没有块级作用域的概念,为了解决这个问题,我们可能会是使用函数,但是函数的目的是进行代码复用。所以我们进行产生IIFE的函数表达式去解决这个问题。IIFE的目的是为了隔离作用域,防止污染全局命名空间。
● 改变函数的执行环境
函数名.call(执行环境对象,实参列表);
函数名.apply(执行环境对象,实参列表数组);//参数是一个数组
函数名.bind(执行环境对象)(实参列表);//返回的是一个函数
● 闭包
指有权访问另一个函数作用域中的变量的函数,闭包的创建方式,就是在一个函数内部创建另外一个函数。
闭包函数有三个特性:
函数内嵌套函数、函数内部引用外部的变量、参数和变量不会被垃圾回收机制回收。
function f1(){
var num = 0;
return function(){
var n = 0;
console.log(++num);
console.log(++n);
}
}
var f = f1();
f();//num为1 n为1
f();//num为2 n为1
//函数在执行完毕,默认是会销毁自身和自身变量的。
//但是因为闭包的机制 匿名函数引用着f1的num,无法被垃圾回收机制回收。
如果我们想解决这个问题,可以采用IIEF的方式,看下面的例子:
function outer(num){
var result = [];
for(var i=0;i<num;i++){
//result[i] = function(){console.log(i)}//10个10
result[i] = (function(num){
return function(){
console.log(num)}
})(i);//0-9
}
return result;
}
var arr=outer(3);
for(var i=0;i<3;i++){
arr[i]()
}
闭包的好处与坏处
好处
①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
③匿名自执行函数可以减少内存消耗
坏处
①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;
②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响
● 数组检测
arr instanceof Array //结果为true,在同一个全局作用域下可以这么判断
Array.isArray(arr); //结果为true,判断arr是否是数组类型
● 数组序列化(反序列化)
var arr = ["terry","larry","boss"];
arr.toString() //terry,larry,boss
var str = arr.join("|"); //terry|larry|boss
var arr = str.split("|"); ["terry","larry","boss"]; //反序列化
● 数组排序方式
arr.sort(function(v1,v2){
if(v1<v2)
return -1
else
return 1
})
for(var i=0; i<arr.length-1;i++){
//排几次
for(var j=0;j<arr.length-i-1;j++){
//每轮排序的项的索引
//如果前面比后面小,则交换2个数的位置
if(arr[j]<arr[j+1]){
//比如第一轮的arr[0]和arr[1]
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
Set
var arr = [1, 1, 4, 2, 2, 3, 3, 3, 6, 6, 6];
arr = Array.from(new Set(arr));
indexOf
var newArr = [];
arr.forEach((item) => {
newArr.indexOf(item) === -1 ? newArr.push(item) : "";
});
includes方法
var newArr = [];
arr.forEach((item) => {
newArr.includes(item) ? "" : newArr.push(item);
});
● 数组的深浅拷贝
浅拷贝(数组): (会改变原数组)
就是数组A重新赋值给数组B,数组B里的属性值被改变,则数组A里的值也会跟着发生改变。
深拷贝(数组): (不会改变原数组)
(1)使用slice() 和 concat()方法来实现深拷贝
(2)利用循环实现
● 可迭代或类数组转换为真数组
var obj = {
0:'zhangsan',
1:'lisi',
2:'wangwu',
length:3
}
● 栈,队列方法(改变原数组)
栈 LIFO (Last-In-First-Out)
pop() 移除数组中的最后一个项并且返回该项,同时将数组的长度减一,
push() 可接受任意类型的参数,将它们逐个添加到数组的末尾,并返回数组的长度
队列 FIFO (First-In-First-Out)
shift() 移除数组中的第一个项并且返回该项,同时将数组的长度减一
unshift() 在数组的前端添加任意个项,并返回数组的长度
● 数组截取拼接
concat():数组拼接,先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾
slice():接收2个参数(start_index,end_index),返回该截取返回的元素。不会改变原数组
var arr=[13,1,15,7,52,34,27,21];
var arr3 = new_arr.slice(0,3);//13,1,15
splice():接收3个参数(start_index,length,替换或追加的元素)。会改变原数组,返回选中的元素。
var arr=[13,1,15,7,52,34,27,21];
arr.splice(2,3,8,8,8);//13, 1, 8, 8, 8, 34, 27, 21
var res = arr.splice(2,3,8,8,8);1,15,7
● 数组的迭代方法
every 全部成员都满足,才会返回true
var arr=[13,1,15,7,52,1,34,27,21,0];
//判断元素是否都大于0
var res = arr.every(function(v,index,arr){
return v>=0;
})
some 只有有一个成员满足,就会返回true
//判断成员中是否有满足18岁
res = arr.some(function(v){
return v>=18;
})
filter 过滤器
//获取满足18岁的所有成员
res = arr.filter(function(v){
return v>=18;
})
map方法,有返回值
//所有成员岁数+1
res = arr.map(function(v){
return v+18;
})
foreach方法,无返回值
//打印所有成员
res = arr.foreach(function(v){
console.log(v)
})
● 可迭代对象
可迭代(Iterable) 对象是数组的泛化。这个概念是说任何对象都可以被定制为可在for…of循环中使用的对象。
很多其他内建对象也都是可迭代的。例如字符串也是可迭代的。
如果我们希望一个对象是可以迭代(也可以是自定义对象),我们必须为对象添加一个Symbol.iterator方法,这个方法必须返回一个 迭代器(iterator) —— 内部一个有next方法的对象。
range[Symbol.iterator] = function() {
return {
current: this.from,
last: this.to,
next() {
// 4. 它将会返回 {done:.., value :...} 格式的对象
if (this.current <= this.last) {
return {
done: false, value: this.current++ };
} else {
return {
done: true };
}
}
};
};
原始就具有迭代器接口的数据类型有:Array、Map、Set、String、TypedArray、arguments、NodeList
● JS的文件读取
//以读取文本为例
f.onchange = function(){
var read = new FileReader();
read.onload = function(){
sessionStorage.setItem('res',read.result);
}
read.readAsText(this.files[0]);
}
● JavaScript 的装箱与拆箱
所谓装箱,就是把基本数据类型转换为对应的引用类型的过程。而拆箱与装箱相反,即把引用类型转换为基本的数据类型。
当我们使用一个基本数据类型,它不是对象,而可以使用原型方法。实际上它经历了:
var str1 ='xiaoming';
var str2 = new String('xiaoming');
var str3 = Srting('xioaming');
function Animal(name){
return name;
}
console.log(typeof str1.valueOf);
console.log(typeof str2)
consoel.log(str1==str2)
console.log()
● JS中字符串对象的方法(String)
var str = 'xiaomiang';
console.log(str.length);//获取字符串的长度
console.log(str.charAt(2));//返回给定位置的那个字符
console.log(str.charCodeAt(2)); //返回给定位置的那个字符编码
console.log(str.indexOf('a'));//返回给定字符的位置
console.log(str.lastIndexOf('a'));//返回给定字符的位置(反向查找)
console.log(str.concat('xiaohong'));//字符串拼接,返回新的字符串
console.log(str.slice(2,4));//字符串截取ao(start_index,end_index);
console.log(str.substr(2,2));//字符串截取ao(start_index,length);
console.log(str.substring(2,4));//字符串截取ao(start_index,end_index);
console.log(str.trim());//去除某个字符串两侧的空格
console.log(str.toLowerCase());//将字符串的字母转换为小写
console.log(str.toUpperCase());//将字符串的字母转换为大写
● js中数学对象的方法(Math)
console.log(Math.min(2,4,6,2,1,7));//其中最小的那个数
console.log(Math.max(2,4,6,2,1,7));//其中最大的那个数
var num = 10.41;
console.log(Math.ceil(num));//向上取舍11
console.log(Math.floor(num));//向下取舍10
console.log(Math.round(num));//四舍五入10
console.log(parseInt(num));//10
console.log(Math.random());//获取一个0-1的随机数
console.log(Math.ceil(Math.random()*100));//1-100
console.log(Math.PI);//圆周率
● js中日期对象的方法(date)
var date = new Date();
console.log(date.getFullYear()); //返回2020,2020年
console.log(date.getMonth()); //月 0-11
console.log(date.getDate()); //返回日 1-31
console.log(date.getHours()); //返回小时0-23
console.log(date.getMinutes()); //分钟0-59
console.log(date.getSeconds()); //秒0-59
console.log(date.getDay()); //3 星期3
console.log(date.getMilliseconds());//毫秒
console.log(date.getTime()); //时间戳
● js 创建对象的方式
我们一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码。但 js和一般的面向对象的语言不同,在 ES6 之前它没有类的概念。但是我们可以使用函数来进行模拟,从而产生出可复用的对象创建方式,我了解到的方式有这么几种:
(1)第一种是工厂模式,工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系。
(2)第二种是构造函数模式。js 中每一个函数都可以作为构造函数,只要一个函数是通过 new 来调用的,那么我们就可以把它称为构造函数。构造函数模式相对于工厂模式的优点是,所创建的对象和构造函数建立起了联系,因此我们可以通过原型来识别对象的类型。但是构造函数存在一个缺点就是,造成了不必要的函数对象的创建。
(3)第三种模式是原型模式,因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用。这种方式相对于构造函数模式来说,解决了函数对象的复用问题。但是这种模式也存在一些问题,一个是没有办法通过传入参数来初始化值,另一个是如果存在一个引用类型如 Array 这样的值,那么所有的实例将共享一个对象,一个实例对引用类型值的改变会影响所有的实例。
(4)第四种模式是组合使用构造函数模式和原型模式,这是创建自定义类型的最常见方式。因为构造函数模式和原型模式分开使用都存在一些问题,因此我们可以组合使用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点,但是有一点不足的就是,因为使用了两种不同的模式,所以对于代码的封装性不够好。
● js继承方式及其优缺点
原型链继承的缺点
如果某一个构造函数实例对象修改了原型对象上的属性值和方法,则也会影响其他实例对象。
构造函数的缺点
但没有原型,则复用无从谈起
原型链+借用构造函数的模式
原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
● dom的节点类型
Document类型(9):指向文档树,为window的一个属性
Element类型(1):指向节点(HTML元素)
Text类型(3):指向文本内容(不包含任何HTML代码)
Comment类型(8):指向注释
● dom中node的属性和方法
var a1 = document.getElementById('a1');
console.log(a1.nodeType);//返回节点类型
console.log(a1.nodeName);//返回节点名称
console.log(a1.nodeValue); //返回节点的值
console.log(a1.childNodes);//返回节点的nodelist集合(子节点)
console.log(a1.childNodes[0]);//可以通过下标去获取元素,如果想使用数组方法,记得要先转换成数组
console.log(a1.parentNode);//返回父节点
console.log(a1.previousSibling);//返回上一个兄弟节点(空白文本节点会被识别到)
console.log(a1.nextSibling);//返回下一个兄弟节点
console.log(a1.firstChild);//获取元素中的第一个子元素
console.log(a1.lastChild);//获取元素中的最后一个子元素
console.log(a1.ownerDocument);//返回整个文档节点document
console.log(a1.hasChildNodes());//检测是否是非空节点
● dom中node操作的方法
//追加内容
var p = document.createElement("p");
var p_text = document.createTextNode('xiaoming');
p.appendChild(p_text);
console.log(p);
a1.appendChild(p);
//有参考的追加元素(指定位置的追加),如果第二个参数为null与appendChild等价
var p2 = document.createElement("p");
var p2_text = document.createTextNode('xiaohong');
p2.appendChild(p2_text);
a1.insertBefore(p2,a1.childNodes[2]);
//替换元素
var new_text = document.createTextNode('今天天气很好,小蓝哭了!')
a1.replaceChild(new_text,a1.childNodes[1]);
//删除元素
a1.removeChild(a1.childNodes[0]);
a5.normalize();//删除2个文本节点中间的空格然后合成一个文本
var b1 = a1.cloneNode(true);//默认为浅拷贝,可以指定值为true进行深拷贝
//拷贝时,不建议元素本身有事件,在拷贝前建议移除元素的事件
● document的属性和方法
console.log(document.documentElement);//HTML
console.log(document.body);//body
console.log(document.doctype);//版本信息
console.log(document.title);//网页标题
console.log(document.URL);//当前的URL地址
console.log(document.domain);//取得域名 需要发布到服务器才可以获取
console.log(document.referrer);//链接到当前页面的页面URL
console.log(document.images);//取得所有的IMG对象
console.log(document.forms);//取得所有的表单元素
console.log(document.links);//取得所有的A元素
var bs = document.getElementsByTagName('b');//获取B标签
var names = document.getElementsByName('username'); //获取有username名称的控件
bs[0].onclick=function(){
console.log(111);
}
names[0].value="xiaoming";
var classs = document.getElementsByClassName('red');//获取有red类的元素
for(var i = 0; i<classs.length;i++){
classs[i].style.color = 'red';
}
● element的属性和方法
console.log(a1.id);
console.log(a1.title);
a1.className="blue";//element的属性可以获取也可以直接进行设置
console.log(a1.src);
console.log(a1.alt);
a1.style.color = 'red';//设置元素的样式
console.log(a1.getAttribute('id'));//获取某个元素的某个属性,参数为属性名
a1.setAttribute('title','小明日记');//设置某个元素的属性
console.log(a1.children);//子元素集合
console.log(a1.firstElementChild);//第一个子元素
console.log(a1.lastElementChild);//最后一个子元素
console.log(a1.nextElementSibling);//下一个兄弟元素
console.log(a1.previousElementSibling);//上一个兄弟元素
console.log(a1.childElementCount);//子元素个数
console.log(a1.innerHTML);//返回标签中的HTML内容,当使用它去设置一个元素的内容时,我们需要去考虑填写的字符串是否为正确的HTML代码的问题!
console.log(a1.innerText);//返回标签中的文本内容,获取每个标签中的文本,单独一行
console.log(a1.textContent);//返回标签中的文本内容,会考虑元素文本的格式,不去除空格和回车
● 文本节点的属性和方法
var text = document.createTextNode('xiaoming');
text.appendData('kule');
/*
length //文本长度
appendData(text) //追加文本
deleteData(beginIndex,count) //删除文本
insertData(beginIndex,text) //插入文本
replaceData(beginIndex,count,text) //替换文本
splitText(beginIndex) //从beginIndex位置将当前文本节点分成两个文本节点
document.createTextNode(text) //创建文本节点,参数为要插入节点中的文本
substringData(beginIndex,count) //从beginIndex开始提取count个子字符串
*/
● 事件三要素
事件目标(event target)
发生的事件与之相关联或与之相关的对象
事件处理程序(event handler)
处理或相应事件的函数
事件对象(event object)
与特定事件相关且包含有关该事件详细信息的对象
● 事件流
“DOM2级事件”规定了事件流包括三个阶段:事件捕获阶段,处理目标阶段和事件
阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后是事件冒泡。
事件捕获: document->html->body
处理目标: 事件处理
事件冒泡: body->html->document
● dom0级、dom1级、dom2级事件
这里所指的DOM为针对HTML文档的一个API,0-2级为对应的版本。
1998年10月W3C指定了DOM1规范,之前的IE规范被称为DOM0,后来W3C把DOM1升级为DOM2,DOM2级将属性升级为队列,更符合我们编程的逻辑。
区别:主要体现在事件处理方面
比如在DOM0中不允许重复定义2个相同的事件,而DOM2中可以通过addEventListener ()来实现。但是IE浏览器(8版本)以前的表现有一些差异。
需要注意的是IE8以前也有DOM2,比如上面的addEventListener ()方法在IE中则是attachEvent(),IE8以后也可以使用addEventListener ()
所以在项目中,我们可能做一些需要兼容,当然如果你不关心可以不做:
if(typeof btn.addEventListener ==='function'){
btn.addEventListener('click',fn);
}elseif(typeof btn.attachEvent ==='function'){
btn.attachEvent('onclick',fn)
}else{
btn.onclick=function(){
// do something}
}
● 事件绑定
var btn = document.getElementById('btn');
btn.onclick = function(){
//console.log(this);//事件目标
this.title = "按钮";
console.log(111);
}
btn.onclick = function(){
console.log(222);}//使用dom0级事件,多次绑定后面的方法会覆盖前面的方法
function f2(){
console.log(222);}
var btn2 = document.getElementById('btn2');
btn2.addEventListener('click',function(){
console.log(111);
},false)
btn2.addEventListener('click',f2,false);
//dom2级的事件绑定方式,可以多次绑定相同事件,不会被覆盖
btn2.removeEventListener('click',f2,false);//事件解绑,匿名函数不会被解绑
bth2.attachEvent() //2个参数(事件,function(){})如果是IE8会识别这个时间绑定方法dom2
bth2.detachEvent()//IE8中移除事件的方法dom2
● event对象
var lis = document.getElementsByTagName('li');
var u1 = document.getElementById('u1');
//事件代理(事件委托)
u1.onclick=function(e){
for (var i=0;i<lis.length;i++)
lis[i].style.background="none";
e.target.style.background="red";
// console.log(111);
}
var l = document.createElement('li');
var l_text = document.createTextNode('《夜的第七章》');
l.appendChild(l_text);
u1.appendChild(l);
lis[0].onclick = function(e){
// e.preventDefault();//取消事件的默认行为
// e.stopPropagation();//阻止冒泡
console.log(e);
console.log(e.bubbles);//是否冒泡
console.log(e.cancelable);//是否取消事件的默认行为
console.log(e.currentTarget);//绑定事件的那个元素
console.log(e.eventPhase);//事件的阶段
console.log(e.target);//实际触发事件的元素
console.log(e.type);//事件的类型
e.target.style.background="red";
}
//客户区坐标位置(窗口)
clientX,clientY 事件发生时,鼠标指针在视口中的水平和垂直坐标位置
//页面坐标位置
pageX,pageY 事件发生时,鼠标指针在页面本身而非视口的坐标,页面没有滚动的时候,pageX和pageY的值与clientX和clientY值相等
//屏幕坐标位置 screenX,screenY
● 取消事件冒泡
//标准写法
e = e || window.event;
if(e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
● 事件类型
load 元素加载完毕后触发,适用于window或者文件加载。
unload 当页面完全卸载后在window上触发,当所有框架都卸载后在框架集上触发。
select 当用户选择文本框(,)中的一个或多个字符时
change 当控件的值发生更改的时候触发
resize 当浏览器窗口被调整到一个新的高度或者宽度时,会触发
scroll 当用户滚动带滚动条的元素中的内容时,在该元素上触发resize,scroll会在变化期间重复被激发,尽量保持代码简单
blur 元素失去焦点的时候触发
focus 元素获得焦点的时候触发,不支持冒泡
//IE支持
focusin 与focus等价,支持冒泡
focusout 与blur等价,支持冒泡
click 点击主鼠标按钮或者按下回车按键的时候触发。只有在一个元素上相继发生mousedown,mouseup事件,才会触发click事件
dblclick 双击主鼠标按钮时触发
mousedown 任意鼠标按钮按下时触发
mouseup 释放鼠标按钮触发
mousemove 鼠标在元素内部移动的时候重发触发
mousewheel 滚轮事件
mouseenter 鼠标光标从元素外部首次移动到元素范围内激发,不冒泡。 【不支持子元素】
mouseleave 在位于元素上方的鼠标光标移动到元素范围之外时触发,不冒泡【不支持子元素】
mouseover 鼠标位于元素外部,将其首次移入另一个元素边界之内时触发 【支持子元素】
mouseout 在位于元素上方的鼠标光标移入到另外一个元素中。【支持子元素】在被选元素上与mouseleave效果相同
keydown 按下键盘任意键时触发,如果按住不放会重复触发此事件
keypress 按下键盘字符键时触发,如果按住不放会重复触发此事件
keyup 释放键盘上键时触发
当键盘事件发生时,event对象的keyCode属性中会包含一个代码与键盘上的特定键对应,对数字字母键,keyCode属性的值与ASCII码中对应的小写字母和数字编码相同
● 定时调用和间歇调用
//超时调用
var id = setTimeout(function(){
alert(1000);
},1000);
clearTimeout(id) //清除
//间歇调用
var id = setInterval(function(){
alert(1000);
},1000);
clearInterval(id) //清除
● location
//页面跳转
// window.location = 'http://www.baidu.com';
// document.location = 'http://www.baidu.com';
//location = 'http://www.baidu.com';
location.href = 'http://www.baidu.com'//跳转,会产生历史记录
//location.assign('http://www.baidu.com')//跳转,会产生历史记录
location.replace('http://www.baidu.com')//跳转,不会产生历史记录
//页面的一些URL信息
console.log(location.host)//返回服务名称和端口号
console.log(location.hostname)//不带端口号的服务器名称
console.log(location.href)//当前地址
console.log(location.pathname)//目录名和文件名
console.log(location.port)//端口号
console.log(location.protocol)//协议名
console.log(location.search)//查询字符串 ?name=zhangsan&age=13
//刷新
location.reload();//刷新,falsa调用缓存进行刷新,true会从服务器重新加载一次页面
location.href="原地址" //刷新
● history
console.log(history.length);//查看当前的历史记录数
function back(){
//history.back();//后退
history.go(-1);
}
function forward(){
history.forward();//前进
history.go(1);//如果值为1,如上方方法等价,也可以指定大于1的页数,如果值为0,则为刷新
}
● open()
var myWin = window.open("http://www.baidu.com",'baidu','width=500,height=400,scrollbars=no');
// myWin.close(); //调用close函数关闭新打开的网页
// myWin.resizeTo(500,500); //调整大小
// myWin.moveTo(300,300); //移动位置