判断变量类型,六种返回结果为(这些值全是字符串类型),number,string,boolean,object,undefined,function
undefined == null,返回结果为true
NaN == NaN,返回false
预编译四部曲:
创建AO(Activation Object)
AO{
}
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
AO{
a: undefined,
b: undefined
}
将实参值和形参统一
AO{
a: 1,
b: undefined
}
在函数体内找函数声明,值赋予函数体
AO{
a: function a(){},
b: function (){},
d: function d(){},
}
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}//函数声明
console.log(a);
var b = function (){}//函数表达式
console.log(b);
function d(){}
}
fn(1);
打印结果:
ƒ a(){}
123
123
ƒ (){}
总结一点就是:在预编译阶段声明的提升,在声明提升过程中同名的也会被覆盖
同时还要注意函数声明和函数表达式不同
[[scope]]:我们所说的作用域,里面存储了运行期上下文的集合。
每个函数都是一个对象,对象中有些属性可以访问,有些不可以。仅供引擎存取,[[scope]]就是一个外界不可访问的
作用域链:运行期上下文的集合所呈现的链式连接称为作用域链
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用链不释放,造成内存泄漏
闭包的作用:
实现公有变量
//累加器
function add(){
var num = 0;
function a(){
console.log(++num);
}
return a;
}
var myAdd = add()
myAdd()
myAdd()
myAdd()
输出:1 2 3
可以做缓存(存储结构)
//类似缓存
function test(){
var food = "";
var obj = {
eatfoot : function(){
if(food != ""){
console.log('我吃' + food);
food = "";
}else{
console.log('没有给我食物');
}
},
pushfood : function(myfood){
food = myfood;
}
}
return obj;
}
var person = test()
person.pushfood("banana")
person.eatfoot()
//共用food变量
可以实现封装,属性私有化
function Person(name,wife){
var prepareWife = "张三";//私有化的属性
this.name = name;
this.wife = wife;
this.divorce = function(){
this.wife = prepareWife;
}
this.changePrepareWife = function(target){
prepareWife = target;
}
this.sayPrepareWife = function(){
console.log(prepareWife)
}
}
var person = new Person("李斯","玛丽");//对象只能通过它的divorce方法,访问私有属性,无法直接访问
模块化开发,防止污染全局变量
闭包造成的问题:
function test(){
var arr = [];
for(var i = 0; i<10; i++){
arr[i] = function (){
document.write(i+" ");
}
}
console.log(arr);
return arr;
}
var myarr = test();
for(var j = 0; j<10; j++){
myarr[j]();
}
打印10个10
因为arr数组中的每个函数都是`function (){
document.write(i+" ");
}`这个i在数组返回时,是没有给值的
返回出来这个arr在test函数外部,共享闭包内(test内)的变量,所以可以共享到i的值为10
要解决这个问题(使用立即执行函数):
解决思路:里面的函数拿到的数据是立即执行函数的结果,而不是test了,而立即执行函根据循环有多个,所以里面的函数分别在自己的作用域里,互不影响
(function(j){
arr[i] = function (){
document.write(j+" ");
}
}(i))
对象
var obj = {}
包装类
基本变量增加属性赋值会先执行包装类
var num = 4;
num.a = 6
//隐式 new Number().a = 6,所以赋值后对原始值没影响
原型:是function对象的一个属性,它定义了构造函数制造对象的公共祖先,通过构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象
构造方法自带的一个属性prototype
prototype对象中的constructor方法,返回构造器,并且可以手动更改。
new的时候会在构造函数中隐式生成一个this,该this就是创建的对象
原型的增删改查:
增,不能在对象中增加,只能在原型中
Person.prototype.lastName = "a"
function Person(){
}
var person = new Person()
person.fine = "b"//添加不到原型,只会给person对象增加自己的fine 属性
Person.prototype.fine = "b"//可以添加到原型
删,无法删除
改,不能在对象中更改,只能在原型中
Person.prototype.lastName = "a"
function Person(){
}
var person = new Person()
person.lastName = "b"//更改不了原型,只会给person对象增加自己的lastName属性
Person.prototype.lastName = "b"//可以更改原型
查,对象访问自己没有的属性,就会去原型中寻找
原型链:绝大多数对象最终都会继承自Object prototype
使用Object.create(null)创建的对象没有原型
作用,是改变this的指向
function Person(name,age){
this.name = name;
this.age = age;
}
var person = new Person('wang',22);
var obj = {}
Person.call(obj,'zhang',88);//obj会使用person的构造函数创建自己
//类似于java的调用父类
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name, age, sex, tel, grade){
Person.call(this,name,age,sex);//类似与Java的调用父类构造方法
this.tel = tel;
this.grade = grade;
}
var student = new Student('zhang',34,'男',180,2019);
调用toString方法的call形式Object.prototype.toString.call(obj)
区别,后面传的参数形式不一样
Person.apply(this,[name,age,sex]);
apply传参得是数组
原型链的方式
借用构造函数(使用call或apply)
共享原型,(子.prototype = 父.prototype)
圣杯模式
function inherit(Target,Origin){
function F(){};
F.prototype = Origin.proptype;
Target.protetype = new F();
Target.prototype.constructor = Target
}
//优化结构
var inherit =(function(){
var F = function(){};
return function(Target,Origin){
F.prototype = Origin.proptype;
Target.protetype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.proptype;
}
}());
callee
arguments的属性。使用arguments.callee,代表函数的引用,在递归中,如果函数没有名字可以使用这个来代替引用
caller
函数的属性,表示在哪个环境调用了该函数(ES5标准模式不让用)
new Array(10)代表长度为10 的稀疏数组(值为undefined)
而new Array(10, 10),代表两个元素为10的数组
js中的数组访问越界的值,返回undefined,不会报错
js中的数组在写越界的数组值时,是可以的,会把数组撑开到要写的长度
数组中的方法:
改变原数组
push,给数组末尾添加一个或多个元素,参数为添加的元素,返回数组的长度
Array.prototype.push = function(target){
this[this.length] = target;
this.length++;
}
pop,将数组末尾元素剪切,不用传参数,并返回
shift,将数组开头元素剪切,不用传参数,并返回
unshift,给数组开头添加一个或多个元素,参数为添加的元素,返回数组的长度
sort,排序数组元素,按照ascill码。参数可以提供一个函数,我们可以自定义排序的规则
arr.sort(function(a,b){
if(a>b){
return 1;//为正时,俩数互换位置
}else{
return -1;//为负时,位置不变
}
});
reverse,反向数组元素
splice,切片数组元素,参数(从第几位开始(可以为负数),截取多少位,在切口处添加新数据),返回截取值
不改变原数组
var obj = {
"0":'a',
"1":'b',
"2":'c',
"length":3,
"push":Array.prototype.push,
"splice":Array.prototype.splice//加上splice属性,打印就和数组一样了
}
类数组条件:
es5.0严格模式的启动,在页面顶端添加"use strict"
,也可以写在局部
开启后,不在兼容es3.0的一些规则语法,使用全新的es5.0规范
不被支持的语法:
定义了表示和修改文档所需的方法。用来操作html和xml的集合
对文档中元素的操作:
节点类型:
节点的四个属性:
节点的一个方法:
遍历节点树(包括所有节点):
基于元素节点树的遍历(只包含元素节点):
元素节点的属性和方法:
以下是构造函数的继承关系
Node
在构造函数的原型中添加属性,相应的元素中也继承该属性
HTMLBodyElement.prototype.abc = "123"
var body = document.getElementByTagName("body")
//body.abc应该也是123
Document中定义的常用属性
全局对象window上的方法,内部this指向window
查看滚动条滚动距离:
查看滚动条IE8及以下方案:
document.body.scrollLeft
document.body.scrollTop
document.documentElement.scrollLeft
document.documentElement.scrollTop
这两个方式使用时相加document.body.scrollLeft + document.documentElement.scrollLeft
,因为兼容性问题,一个可以使用,另一个必然为0
让滚动条滚动:
查看窗口尺寸:
查看窗口尺寸IE8及以下方案:
标准模式和怪异模式的区别,标准模式在html文档顶部添加
判断页面渲染模式:
document.compatMode
//结果为:CSS1Compat,则是标准模式
//结果为:BackCompat,则是怪异模式
查看元素几何尺寸:
返回有定位的父级:
读写元素的CSS属性:
查询计算样式:
查询计算样式IE8以下方案:
绑定事件函数:
解除事件处理函数:
ele.addEventListener(事件类型,处理函数,false)
中的第三个参数决定了是捕获还冒泡
事件执行的那个元素不会冒泡和捕获,冒泡捕获只会发生在父级
如果同一个事件绑定两个处理函数,分别绑定了冒泡和捕获模式,则触发顺序,先捕获,后冒泡
focus,blur,change,submit,reset,select等事件不冒泡
取消事件冒泡:
在事件处理函数里面写如下,则不发生冒泡(evnet为事件处理函数的参数,系统传递 )
阻止默认事件:
事件对象:
事件源对象的使用:
外部元素绑定事件,在单击其内部元素时,通过其事件源对象可以知道单击的哪个对象,不用里面每个元素都绑定事件
//优点:
//1、不需要循环所有元素一个一个绑定事件
//2、当有新元素添加时,不需要重新绑定
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
var ul = document.getElementsByTagName('ul')[0]
ul.onclick = function (e) {
console.log(e.target.innerText)
}
</script>
</body>
鼠标左右键的区分,使用事件对象的button属性event.button
click事件只能监听左键,不能监听右键,判断左右用mousedown或mouseup
正常js加载是同步的,要等页面加载完再加载,因为js要操作页面,然后有时一些js不操作页面,这时候还是用同步,就显得效率低
异步加载的三种方案:
defer,使用方法,给script标签增加属性defer。
async,使用方法,给script标签增加属性async。
(重点)创建script,插入到dom中,加载完毕后callBack
var script document.creatElement('script');
script.type = "text/javascript";
script.src = "tools.js";//开始下载js
document.head.appendChild(script);//添加到页面,才执行js
script.onload = function(){
//tools加载完后,才能调用里面的方法,不然报错
}
//IE中没有load使用readyState
script.onreadystatechange = function(){
//里面判断readyState的变位情况,做出操作
}
两种创建方式:
直接量
var str = "abcd";
var reg = /abc/i;//i修饰符,代表忽略大小写,也可以不写
reg.test(str)//返回布尔值,表示是否匹配
new RegExp()
var str = "abcd";
var reg = new RegExp("abc","i")//i修饰符,代表忽略大小写,也可以不写
reg.test(str)//返回布尔值,表示是否匹配
两个方法:
修饰符:
方括号:
使用方括号代表一位[0-9],里面的内容是取值范围
元字符:
. === 查找单个字符,除了换行和行结束符
\w === [0-9A-z_]
\W === 非\w
\d ===[0-9]
\D === 非\d
\s === [空格 制表符 回车符 换行符 垂直换行符 换页符]
\S ===非\s
\b === 单词边界
\B === 非单词边界
\n === 查找换行符
\f === 查找换页符
\r === 查找回车符
\t === 查找制表符
\v === 查找垂直制表符
\xxx === 查找八进制数xxx规定的字符
\xdd === 查找十六进制数dd规定的字符
\uxxxx === 查找以十六进制数xxxx规定的Unicode字符
\数字 === 匹配第几个子表达式相同的内容,子表达式就是圆括号中的
例如:
var str = "aabb"
var reg = /(\w)\1(\w)\2/g
任何区间的匹配使用[\s\S]或[\w\W]等等,加个就是任意多个了*
量词(匹配数量):
RegExp对象属性:
RegExp的对象方法:
支持正则字符串对象方法:
例子:
1、将匹配到的aabb调换为bbaa
//小括号内为子表达式
var reg = /(\w)\1(\w)\2/g;
var str = "aabb";
var result = str.replace(reg,"$2$2$1$1")//此处的第二个参数可以引用正则表达式的子表达式,$1为第一个子表达式,$2为第二个
//replace第二个参数也可以函数
//$,正则匹配的结果
//$1,第一个子表达式匹配内容
//$2,第二个子表达式匹配内容
var result = str.replace(reg,function($,$1,$2){
return $2 +$2 +$1 +$1;
})
2、将the-first-name变成小驼峰theFirstName
var reg = /-(\w)/g;
var str = "the-first-name";
var result = str.replace(reg,function($,$1){
return $1.toUpperCase();
});
打破贪婪模式,在量词后面加问号?