编译性语言:c c++有中间文件生成,最后直接执行中间文件
解析性语言:javascript php无中间文件生成,翻译一句执行一句
java是通过jvm翻译
同步:计算机里面同步指的是非同一时间进行的事件
异步:计算机里面的异步指的是同时进行的事件
多线程:一个执行体同一时间可以做多件事
单线程:一个执行体同一时间只能做一件事
JavaScript是解释性语言,单线程
js执行线程:轮转时间片,类似于吃饭,一共3碗菜1碗饭,一口一口吃最后都吃完了,分成一点一点随机执行
js三部分:ECMAScript、DOM、BOM
主流浏览器(有自己的内核):
IE trident
Chrome webkit/blink
firefox Gecko
Opera7之前 presto
Safari webkit
<!-- js引入方法一:页面级js代码,可以写在任意位置
<script type="text/javascript">
document.write('hello world');
</script> -->
<!-- js引入方法二:外部js引入 -->
<!-- <script src="js/js01.js"></script> -->
数据类型:
- 原始数据类型,存在于栈
- string 字符串
- boolean
- number
- undefined
- null
- symbol 符号,es6新增的一种数据类型,使用let a = Symbol('bbb'),独一无二
- 引用数据类型
- Object
- Array
- Date
- Function
- RegExp
<!-- 原始值 stack(栈) 栈相当于裤兜,先放进去的最后出来
var num=100;
num1=num;新开一个房间把值的副本复制过来
num=200;
引用值 引用的是地址 heap(堆) 值存堆里面,栈里面存的是堆的地址
var arr=[1,2];
arr1=arr;直接指向arr的地址
arr.push();插入一个值
arr=[1,3];开一个新房间
错误:
低级错误,程序直接一行都不执行,如果掺杂中文字符
逻辑错误,哪出现错误后面的都不执行,前面会执行,如变量未定义
html页面中的各script是相通的,可以互相引用,但一个script中出现错误不会影响其他script,但是只能后面的script引用前面的script
数值运算符
NaN not a number NaN不等于自己
运算
任何字符串运算最后都是字符串
+可以做数学运算也可以链接字符串,运算方法是自左向右
-、*、/、%、=最弱、()最强,++,-- ,+=,-=
++ --在前先运算然后执行语句
++ --在后先执行语句然后运算
var a= 100;
var b= 200;
交换ab之间的值
方法一借助第三个参数c
var c=a;
var a=b;
var b=c;
方法二通过加减运算
a=a+b;
b=a-b;
a=a-b;
1 / 0 infinity无穷大
0 / 0 NaN
-1 / 0 -infinity无穷大
比较运算符 运算值是Boolean值
字符大小比较的是ascll码 A是65 a是97 数字0是48 字符串是一个一个比
< > == >= <= !=
逻辑运算符
&& 返回原本的值,先判断第一个值是真是假,如果是真就直接返回第二个值,如果第一个是假就直接返回这个假的值
直接返回假的,到假就返回,没假返回最后的
&&前面是真才会执行后面的,可以作为一种控制语句使用,取名短路语句
碰到假就返回
2 && 1
||
先判断第一个值,如果是真就直接返回,如果是假就判断第二个值,返回的都是判断的值
碰到真就返回
2||1
适用于兼容性,ie浏览器取值windows.event,其他浏览器取值e,其实两个值是一样的,因为浏览器差异而导致的,解决此问题就可以使用||碰到真的就返回
undefined null NaN "" 0 false ===>false
!非运算,返回值是Boolean值
/*
计算2的n次幂
var sum=1;
var n = window.prompt('计算2的n次方,请输入自然数n');
for(;n > 0;n--){
sum *= 2;
}
document.write(sum);
for(var i = 0; i<n; i ++){}
*/
/*
var n = window.prompt('计算n的阶乘,请输入自然数n');
var sum = 1;
for(;n > 0;n --){
sum = sum * n;
}
document.write(sum);
for(var i = 1; i<=n; i ++){}
*/
/*
第n项的斐波那契值
var f1 = 0;
var f2 = 1;
var f3 = 0;
var n = window.prompt('计算第n项的斐波那契值,请输入自然数n');
for(;n > 0;n --){
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
document.write(f1);
*/
/*
反向输出3位数n
var n = window.prompt('反向输出n,n是3位数,请输入自然数n');
a = n % 10 * 100;
b = parseInt(n / 10) % 10 * 10;
c = parseInt(n / 100);
document.write(a+b+c);
*/
/*
比较输入的3个数大小
var a = window.prompt('请输入数字a的值');
var b = window.prompt('请输入数字b的值');
var c = window.prompt('请输入数字c的值');
if(a > b && a > c){
document.write(a);
}else if(b > a && b > c){
document.write(b);
}
else{
document.write(c);
}
if(a > b){
if(a > c){
document.write(a);
}
else{
document.write(c);
}
}
else{
if(b>c){
document.write(b);
}
else{
document.write(c);
}
}
*/
// window.οnlοad=function (){
// var a = parseInt(window.prompt('请输入数字a的值'));
// var b = parseInt(window.prompt('请输入数字b的值'));
// var c = parseInt(window.prompt('请输入数字c的值'));
// console.log(typeof a);
// // console.log(a);
// // console.log(b);
// // console.log(c);
// if(a > b){
// if(a > c){
// document.write(a);
// }
// else{
// document.write(c);
// }
// }else{
// if(b > c){
// document.write(b);
// }
// else{
// document.write(c);
// }
// }
// };
// if 满足条件后还会去判断
// else if满足条件后不去判断其他的
// else
/*
for (var i = 100; i --; ) {
document.write(i+' ');
}
for语句>>>先执行一遍,然后判断2执行3,判断里面是有if的
while(条件){
执行语句
条件变化
}
do{
执行语句
}while(条件)
*/
// Math.sqrt();//开方函数
/*
var n = 1;
switch(n){
case 1:
console.log("a");break;
case 2:
console.log("b");break;
case 3:
console.log("c");break;
}
break;//功能是终止循环
continue;//功能是终止本次循环,进行新一次循环
*/
//数组
/*
var arr = [0,1,11,4,5,5,30];
//arr[0]
//arr[0] = 1
for(var i = 0; i < arr.length; i ++){
console.log(arr[i]);
}
*/
//对象
/*
var obj = {
name : "nick",
age : 20,
father : "big nick"
}
console.log(obj.name);
obj.name = "old nick";
console.log(obj.name);
*/
/*
1.面向过程 机械性 一步一步做
2.面向对象 依赖解决问题的方法
*/
/*
typeof()//分析数据类型 number string Boolean object(数组,null,对象) undefined function
1.显示类型转换
Number('123');//转换成数字,看起来不是数字的字符串无法转成数字number类型,undefined会转成NaN看起来不是数字的字符串会转成NaN,null会转成0
parseInt();//把数值转换成整型
parseInt(demo,16);//把值按进制转换成相应数值
parseInt("123abc");//可以取出数字123
parseFloat();//转换成浮点型
parseInt("123.222abc");//可以取出数字123.222
String()//转换成字符串类型
Boolean()//转换成布尔类型
toString()//转换成字符串,null和undefined不能使用
isNaN();//会把括号里面的值放到Number()里面然后跟NaN对比,隐式调用Number
null==undefined都不是数字
NaN自己不等于自己NaN
===绝对等于
!==绝对不等于
变量未定义会报错,只有一个情况不报错就是该未定义变量放在typeof()里面不报错返回值是undefined
typeof()返回值是字符串 number string boolean object(数组,null,对象) undefined function都是字符串
*/
//console.log(Number(null));//返回0
/*
var a = "123";
a ++;//会调用Number()把a转成数字
'a' + 1//隐式转换字符串
== !=//有隐式转换
*/
//console.log(typeof(a+""));
//var a="abc123";
//alert(typeof(undefined));
// alert("11"+11);
//alert(typeof(typeof(a)));
//var num = 1231230.123456;
//alert(num.toFixed(3));
//var a = 10 + "20";
//document.write(a);
alert(NaN==undefined);
num.tofixed(3);//保留3位小数并四舍五入
//函数 function 高内聚 低偶合 定义一个功能
//定义函数
function test(){
}
test();//调用函数
函数命名规范 小驼峰 第一个单词小写后面的单词首字母都大写
test代替是整个函数体
第二种函数定义
命名函数表达式 abc是函数名字,test代表函数体
test.name = abc
var test = function abc(){
}
匿名函数表达式 --- 函数表达式 demo代表函数体和函数名
var demo = function () {
}
//sum(a,b)里面的ab表示形式参数---形参 相当于var a,b;
function sum(a,b){
var c = a + b;
}
//sum(1,2)里面的叫实际参数----实参
sum(1,2);
//形参和实参不需要数量上对应,可多可少
//函数里面有个叫实参列表的东西叫arguments数组,他会把实参全部放到这个数组里面,arguments.length
//形参里面也有一个存形参的功能,直接是函数名,也是个数组sum.length
//形参多于实参时,有多少实参就映射多少个实参,实参列表出生的时候有多少就是多少不会再改变
//return第一个功能是终止函数,第二个是返回值
function sum(){
return 123;
}
//要定义一个变量来接收返回值
var a = sum();
//写一个函数计算任意个数的和
var result = 0;
function sum(){
for(var i = 0;i < arguments.length;i ++){
result += arguments[i];
}
console.log(result);
}
sum(1,2,3,4,5,6,7,8,9);
//写一个属于自己的转换函数,把字符串转换成数字
function myNumber(target){
return +target;
}
var num = myNumber('123');
console.log(num);
//写一个函数,功能是告知你所选定的动物的叫声
/*
function talk(select){
switch(select){
case 'A': document.write('汪汪');break;
case 'B': document.write('嚎嚎');break;
case 'C': document.write('喵喵');break;
}
}
var select = window.prompt('A.狗 B.狼 C.猫 请输入您的选择');
talk(select);
*/
//实现加法计数器
/*
function add(a,b){
var c = a + b;
return c;
}
var a = parseInt(window.prompt('请输入第一个数'));
var b = parseInt(window.prompt('请输入第二个数'));
console.log(add(a,b));
*/
//计算n的阶乘
/*
function moreMul(n){
var sum = 1;
for(var i = 1;i <= n;i++){
sum = sum * i;
}
document.write(sum);
}
moreMul(4);
*/
//计算n的阶乘方法二 此方法叫递归
/*
function jc(n){
if(n == 1){
return 1;
}
return n * jc(n - 1);
}
*/
//函数实现斐波那契数列
/*
function feibo(n){
if(n<3){
document.write(1);
}
else{
f1 = 1;
f2 = 1;
for(var i = 2;i < n;i ++){
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
document.write(f3);
}
}
feibo(1);
//求斐波那契数列的方法二 递归
*/
function feibo(n){
if(n == 1 || n == 2){
return 1;
}
return feibo(n - 1) + feibo(n - 2);
}
console.log(feibo(7));
//递归方法规律,找数据规律,找出口,一定要指定出口,没出口就会一直死循环
//出口指的是亘古不变的东西
//递归的先执行的最后返回,因为他要等其他的计算完返回
//字符串也可以一个一个拿出来,相当于数组,可以一个一个取str[],还有个专用的charAt()
//输入数字然后以该数字的中文逆转输出
/*
function reverse(){
var num = window.prompt("请输入数字");
var str = '';
for(var i = num.length - 1;i >= 0;i --){
str += transfer(num[i]);
}
document.write(str);
}
function transfer(target){
switch(target){
case "1" :
return '一';
case "2" :
return '二';
case "3" :
return '三';
case "4" :
return '四';
case "5" :
return '五';
case "6" :
return '六';
case "7" :
return '七';
case "8" :
return '八';
case "9" :
return '九';
case "0" :
return '零';
}
}
reverse();
*/
//全局变量 在js里面定义的变量 整个都可以使用
//局部变量 函数里面定义的的变量 函数里面才能使用,外面不能使用
//外面拿不到里面的,函数1里面的函数2,函数1不能拿到函数1的变量
//并行行数之间的变量也不能访问
js解析性语言(语法分析---预编译(全局预编译,函数预编译)---解释执行)
js三部曲
1.语法分析,通篇看一下代码看看有没有低级错误
2.预编译 提前调用test() 提前输出前面未定义的变量a 这些都是预编译过程
test();
function test(){
console.log('a');
}
console.log('a');
var a = 123;
预编译发生在函数执行的前一刻
预编译四部曲
1.创建AO对象(Activation Object) 执行期上下文 活跃对象
AO{
}
2.找形参和变量声明,将形参和变量名作为AO对象的属性名,值都为undefined
3.形参和实参值相统一,就是把实参的值放到形参里面去
4.在函数体里面找函数声明,函数名作为属性名,函数体作为值
//同名的变量和形参函数声明,函数声明权限最高
//函数里面有预编译,全局也有预编译,全局的叫GO,window===GO
//先GO然后AO,AO上有就使用AO,没有就往GO找,遵循近者优先
//判断时要看清函数在哪开始执行
//预编译总结:
//1.函数声明整体提升,函数出生起就被拿到了最前面去,所以无论在哪调用他都可以执行
//2.变量 声明提升
3. 解析执行
imply global//暗示全局变量 a = 10;默认是全局变量
window.a = 123;//全局对象 表示window{a : 123}
变量都是window全局对象,都可以使用window.b访问
var b = 123;可以使用window.b访问
//window就是全局的域
一切定义在全局的变量都归window所有
未经声明就给他赋值的变量默认是全局变量
window就是全局
window就是一个对象
var a = 123;
var b = 234;
var c = 567;
相当于
window{
a : 123,
b : 234,
c : 567
}
console.log(a);相当于console.log(window.a);
作用域 域scope
每个js函数都是一个对象,对象有些属性我们可以访问,但有些是不可以的,这些属性仅给js引擎存取,[[scope]]就是其中一个
运行期上下文:当函数执行时,会创建一个执行期上下文的内部对象,一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁
[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合
作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合成链式链接,我们把这种链式链接叫做作用域链
函数定义的时候就有GO了,他就存在于a.[[scope]]里面第一个就是-----> 0 : GO{}
函数执行的时候a.[[scope]]就发生变化了,第一个存的是-------------> 0 : AO{}
第二个存的是--------------> 1 : GO{}
查找变量的时候是从作用域链顶端开始查找
闭包
多个函数对应的闭包都是同一个,划线模型在巨人的基础上调用
当内部函数被保存到外部时,将会产生闭包。闭包会导致原有作用域链不释放,造成内存泄露
内存泄露就是内存被占用
闭包的作用
1.实现公有变量,比如累加器
function add(){
var count = 0;
function demo(){
count ++;
console.log(count);
}
return demo;
}
var counter = add();
counter();
2.可以做缓存(存储结构)
/*
例一
function test(){
var num = 100;
function a(){
num ++;
console.log(num);
}
function b(){
num --;
console.log(num);
}
return [a,b];
}
test();
例二
function eater(){
var food = "";
var obj = {
eat : function (){
console.log("I am eating " + food);
food = "";
},
push : function (myFood){
food = myFood;
}
}
return obj;
}
var eater1 = eater();
eater1.push('banana');
eater1.eat();
*/
3.可以实现封装,属性私有化 后面课
闭包作用 私有化变量 除非自己说有,别人不可能知道他有
function Person(name){
var money = 100;
this.name = name;
this.makeMoney = function(){
money ++;
};
this.offer = function (){
money --;
}
this.say = function(){
console.log("I have money" + money);
}
}
var person = new Person();
4.模块化开发,防止污染全局变量 后面课
var name = "bac";
var init = (function (){
var name = "abc";
function callName(){
console.log(name);
}
return function (){
callName();
}//形成闭包,留一个接口等待外面被调用,还不会污染全局变量,这个就是变量私有化
}())
init();
//针对初始化功能的函数也就是正常的函数,需要多次使用的函数
//立即执行函数 执行完一次就被销毁了 所以无需取名 可以有返回值 可以直接用变量接收整个函数
(function (形参){
var a = 123;
var b = 1747;
console.log(a + b);
}(实参))
扩展:只有表达式才能被执行符号执行 ()执行符号 表达式被执行后就忽略了函数名
变成表达式的方法 var test = function.. +function -function !function
立即执行函数表达式被执行后,就会失去对原有函数的索引 test变成undefined了
var test = function (){
console.log("a");
}();
函数声明跟函数表达式要分清,函数声明是不能立即执行的,会报错,只有表达式才能被执行
function test(){
console.log("a");
}();
立即执行函数表达式被执行后,就会失去对原有函数的索引,函数名会失去索引
function test(){
var arr = [];
for(var i = 0;i < 10;i ++){
(function (j){
arr[j] = function (){
console.log(j);
}
}(i))
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j ++){
myArr[j]();
}
//函数声明和函数表达式是两个东西
//
//把整个函数闭包到外部
把函数赋值给变量,系统不会看函数里面,就知道把函数给这个变量,除非要访问这个变量,他才会去执行这个变量里面的东西
function test(){
var arr = [];
for(var i = 0; i < 10; i ++){
arr[i] = function (){
console.log(i);
}
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j ++){
myArr[j]();
}
//arr[i] = function (){console.log(i);}这个是把整个函数存到arr里面,而不会去立马执行这个function (){console.log(i);}
闭包
当内部函数被保存到外部时,将会产生闭包。闭包会导致原有作用域链不释放,造成内存泄露
闭包示例 让里面函数跑到外面去 return 该函数 把改函数赋值给全局变量
function a(){
function b(){
var bbb = 234;
document.write(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
闭包会导致原有作用域链不释放,造成程序内存空间被占用,专业词是造成内存泄露
累加器
function test(){
var count = 0;
function a(){
console.log(++ count)
}
return a;
}
var myAdd = test();
myAdd();
全局变量放到GO
函数定义,形参和局部变量定义放到AO,函数权限最高
逗号运算符 他会运行表达式1然后运行表达式2最后返回表达式2的结果
(表达式1,表达式2)
()运算符会把里面的东西变成表达式,用完一次直接会丢失索引,而且()权限最高
数字加上一个不能进行运算的字符会变成NaN
对象 是一种基础的数据类型 任何事都可以抽象成一个对象 对象可以有属性和方法 对象上的方法就是函数
var mrDeng = {
name : "MrDeng",
age : 40,
sex : "male",
health : 100,
smoke : function (){
console.log('I an smoking !');
mrDeng.health --;//mrDeng==this
},
drink : function(){
console.log('I an drinking !');
mrDeng.health ++;//mrDeng==this
}
}
属性的增加,修改,删除,查询
增加一个属性,直接mrDeng.属性名 = "";
删除需要借助delete 属性名
对象的创建方法
1.var obj = {} plainObject 对象字面量,对象直接量
2.构造函数 有new就会生成一个对象
1.系统自带的构造函数 Object() var obj = new Object();
2.自定义
//大驼峰试命名规则 构造函数遵循大驼峰命名规则
function Person(){}
var person = new Person();
function Car(color){
this.color = color;
this.name = 'BMW';
this.height = "1400";
this.lang = "4900";
this.weight = 1000;
this.health = 100;
this.run = function(){
this.health --;
}
}
var cae = new Car("red");
var car1 = new Car("black");
用户自选car的颜色,就可以新加一个参数放到函数里面
构造函数内部原理 构造函数有了new他就可以生产函数
有new就会出现下面3步
1.在函数体最前面隐式加上this = {} 创建空的this对象
2.执行this.xxx = xxx;
3.隐式返回this
出现new就是构造函数了,new出来的东西就是对象了
new 操作的详细:
1.创建一个空对象obj={}
2.设置对象obj的constructor属性为构造函数,并将obj的proto属性指向构造函数的原型
3.调用构造函数并将构造函数的this指向新对象
4.隐式返回一个obj对象实例
var num = new Number(123);//对象数字123 跟原始值数字是有区别的
var str = new String("abcd");//对象字符串abcd
var bol = new Boolean('true');//对象布尔
原始值数字和字符串、布尔是不能有属性和方法的,但对象数字、字符串、布尔是可以有属性和方法的
//包装类
var num = 4;
num.len = 3;//原始值是不能进行像对象那样的操作,但系统还是不会报错,他还会给你new Number(4).len = 3;但系统这样做完后他会自动进行delete操作,把他又删除了
console.log(num.len);//依然原始值还是没有对象操作,但系统还是会隐式的给你new Number().len
上面的过程就叫做包装类
字符串和数组是系统自带length属性的
Number没有这个属性
大函数里面的小函数只要被弄出了大函数就会产生闭包,产生了闭包,小函数就一直拿着大函数的AO和GO
this和new配合也可以把一个函数弄出来,因为this里面隐式有return
parseInt(3,8);//把3看成是8进制的3转换成十进制
形参和相映射,牵一发而动全神,形参改会改,实参改也会改
原型定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法, 原型也是对象
原型 prototype 他也是个对象 从函数出生起就有这个属性了
Person.prototype ---原型
Person.prototype = {} ---是祖先
只要是对象就可以进行增删改查操作 Person.prototype.name = 'heheh';
所以通过person()构造出的函数就可以继承该对象属性
原型是公有属性 该构造函数构造出的对象都可以继承该公有属性
函数自己也可以新建与原型里面名字相同的属性,构造函数构造出的函数就会拿函数自己有的,而不会去拿原型里面的 遵循就近原则
function Person(){
}
var person = new Person();
通过原型还可以解决代码冗余 把大家都共有的属性提取出来放到原型对象里面
function Car (color){
this.name = "BMW";//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
this.height = 1400;//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
this.length = 4000;//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
this.color = color;
}
var car = new Car("red");
原型里面的东西是比较难修改的,除非通过Person.prototype修改,其他方法是不可能的
new 出来的对象继承的原型是不能删除的,删除后还是存在,除非操作prototype
4.对象如何查看对象的构造函数-----------> constructor 构造出对象后就会自带一个constructor属性
constructor属性是存在原型prototype对象里面里面的一个属性,他存储着构造函数整个函数体
原型里面的属性只能通过对象去修改如Car.prototype.属性 = "";
大驼峰对象函数
function Person (){
//var this = {
// __proto__ : Person.prototype
//}
}
即__proto__ === Person.prototype
但是仅仅是说__proto__和prototype两个人指向的房间(对象)是一样,new的时候才会找__proto__,new了之后对象就被存到变量里面,相当于__proto__是属性而属性值是prototype
原型里面套原型就组成了原型链 原型链连接点就是__proto__ 就近
Object.prototype是原型链的最终端
原型值只有本人有权限修改删除和增加,其他人没有,查询权限大家都有
Grand.prototype.lastName = "Deng";
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
}
var father = new Father();
Son.prototype = father;
function Son(){
}
var son = new Son();
sayName里面的this指向是,谁调用的这个方法,this就是指向谁
示例
Person.prototype = {
name : "a",
sayName : function (){
console.log(this.name);
}
}
function Person(){
this.name = "b";
}
var person = new Person();
对象定义 下面两个是一样 正常都是使用第一个 两个都是有原型的
var obj = {}----对象字面量,对象直接量----系统内部就会给你new Object()
var obj = new Object()
Object.prototype是原型链的最终端
__proto__里面是 Object.prototype
创建对象的另一个灵活方法 -- var obj = Object.create(原型);
示例1
var obj = {name : "sunny",age : 12};
var obj1 = Object.create(obj);
示例2
Person.prototype.name = "sunny";
function Person(){
}
var person = Object.create(Person.prototype);
绝大多数对象最终都会继承自Object.prototype
特例是
Object.create(null);它里面就没有原型
var num = 123;//数字是原始值,他是不能进行对象操作的,
num.toString();//数字进行toString()方法操作,他调用是包装类new Number(num).toString();他使用的是Number对象里面的Number.prototype里面的toString方法
方法的重写 函数名字一样,重新给他写个功能
示例 toString本来是原型里面就有的方法,通过修改后就是方法的重写
Person.prototype = {
toString : function (){
return 'hehhe';
}
}
function Person(){
}
var person = new Person();
示例2
Object.prototype.toString = function (){
return "haha";
}
Object.prototype.toString
Number.prototype.toString
Array.prototype.toString
Boolean.prototype.toString
String.prototype.toString
Object.prototype.toString.call(123);
document.write();//调用的是函数里面的toString()方法
Math.floor();//向下取整函数12.1232325 = 12
Math.ceil();//向上取整函数12.1212 = 13
Math.random()//区间(0,1)随机函数
toFixed(2);//保留2位小数并且四舍五入
bug bug导致原因是js本身精度不准导致的 一般用Math.floor取整 浮点数都存在精度不准的bug js运算精度前16位后16位
0.14 * 100 = 14.00000000002
可计算方法小数点前16位后16位
任何一个方法都可以.call
test();======test.call();这两个是一样的
test();默认函数里面的this指向window
var obj = {}
test.call(obj);这样写的话就改变了this指向,指向obj,所有call的作用就是用来改变this指向
function Person(name,age){
//this == obj
this.name = name;
this.age = age;
}
var person = new Person("deng",100);
var obj = {};
Person.call(obj,'cheng',300);//call改变this指向obj,函数里面就变成obj.name,obj.age,obj对象里面就加入了cheng和300,借用了别人的方法来给自己做事
借用别人已有的 call本质是借用别人的函数来实现自己的功能 想用别人的方法就call他
function Person(name,age){
this.name = name;
this.age = age;
}
function Student(name,age,grade,sex){
Person.call(this,name,age);
this.grade = grade;
this.sex = sex;
}
造车
function Model(height,width,len){
this.height = height;
this.width = width;
this.len = len;
}
function Sit(sitConfortable,sitColor){
this.sitConfortable = sitConfortable;
this.sitColor = sitColor;
}
function.Wheel(style){
this.style = style;
}
function Car(height,width,len,sitConfortable,sitColor,style){
Model.call(this,height,width,len);
Sit.call(this,sitConfortable,sitColor);
Wheel.call(this,style);
}
var car = new Car(1000,1000,1000,"牛皮舒服","red","八大金刚轮胎");
call需要把实参按形参的个数传进去
apply需要穿一个arguments实参列表以数组形式
两者功能是一样的,都是改变this指向,不同之处就是传参列表不同
函数都有原型 原型链
继承发展史 继承原型上的属性
1.传统形式 儿子继承父亲,父亲继承祖父,造成儿子也继承了祖父,但过多的继承导致继承了很多不要的属性
示例
Grand.prototype.lastName = "Deng";
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
}
var father = new Father();
Son.prototype = father;
function Son(){
}
var son = new Son();
2.借用构造函数 call和apply 缺点是不能继承借用构造函数的原型 每次构造函数都要多走一个函数 视觉上是缩减了代码 但运行上是多走了
function Model(height,width,len){
this.height = height;
this.width = width;
this.len = len;
}
function Sit(sitConfortable,sitColor){
this.sitConfortable = sitConfortable;
this.sitColor = sitColor;
}
function.Wheel(style){
this.style = style;
}
function Car(height,width,len,sitConfortable,sitColor,style){
Model.call(this,height,width,len);
Sit.call(this,sitConfortable,sitColor);
Wheel.call(this,style);
}
var car = new Car(1000,1000,1000,"牛皮舒服","red","八大金刚轮胎");
3.共享原型
Father.prototype.lastName = "deng";
function Father (){
}
function Son(){
}
Son.prototype = Father.prototype;
var son = new Son();
写一个函数功能实现两个构造函数共享原型
function inherit(Son,Father){
Son.prototype = Father.prototype;//两个同时指向一个房间,一个改都会改
}
4.圣杯模式 借助一个中间函数
function inherit(Target,Origin){
function F(){}//借助中间函数
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;//归位
Target.prototype.uber = Origin.prototype;//添加一个属性到时候方便自己知道继承的是谁
}
Father.prototype.lastName = "deng";
function Father (){
}
function Son(){
}
inherit(Son,Father);
var son = new Son();
执行以上后
son.__proto__ ---> new F().__proto__ ------->Father.__proto__
所以son.constructor是Father所以为了归位一下son.constructor = Son;
函数被保存到了外部,他就存储了刚才执行上下文
function Deng(name,wife){
var prepareWife = "xiaozhang"
this.name = name;
this.wife = wife;
this.divorce = function (){
this.wife = prepareWife;
}
this.changePropareWife = function (target){
prepareWife = target;
}
this.sayPrepareWife = function (){
console.log(prepareWife);
}
}
var deng = new Deng();
以上Deng函数里面的3个函数被保存到了外部形成了闭包,这就导致了这3个函数拿着Deng的AO和GO到了外部,又因为prepareWife是一个变量而不是属性,只能Deng里面才有这个变量,deng是继承不到这个变量,deng只能继承属性,这就导致了别人是无法知道deng里面是不是有这个变量,这就把prepareWife做了很好的隐藏,这个就是闭包的第三个作用,变量私有化
命名空间 也是一个对象
作用:管理变量,防止污染全局,适用于模块化开发
var org = {
department1 : {
jicheng : {
name : "abc";
age : 12
},
laodeng : {
}
},
department2 : {
zhangsan : {
name : "aaa",
}
}
}
var jicheng = org.department1.jicheng;
jicheng.name就可以使用了
org就是命名空间
var name = "bac";
var init = (function (){
var name = "abc";
function callName(){
console.log(name);
}
return function (){
callName();
}//形成闭包,留一个接口等待外面被调用,还不会污染全局变量,这个就是变量私有化
}())
init();
如果函数不写return那就是默认return undefined
方法的连续调用就可以return this;就可以连续调用了
var obj = {
name : "abc"
}
obj.name ===== obj['name']两个是一样的功能,使用obj.name的时候系统会自动隐式转换为obj['name']
obj[]里面必须是字符串,这样就可以在里面进行字符串拼接,使用就更加灵活了
对象的枚举 遍历就是挨个拿出来这个过程就是个遍历过程,相当于枚举
对象是没有length属性的,所以就无法用for循环一个一个遍历出来 对象枚举的时候会把原型也遍历出来,如果原型里面有属性和值,解决办法是判断一下prop是不是自己的属性,方法是obj.hasOwnProperty(prop)用来过滤一下,因为for的时候都不希望拿到原型里面东西
所有for in循环都会搭配hasOwnproperty()方法
属性是字符串,没加''""是变量
in可以判断是否在obj上 "height" in obj;
解决办法是
var obj = {
name : "nbi",
age : 12,
sex : "male",
}
for(var prop in obj){
console.log(obj.prop);//obj.prop隐式变成obj['prop'],系统就回去找prop这个属性因为prop本来就是一个字符串
}
var prop in obj//系统会把obj里面的属性名传到prop里面去
instanceof
A instanceof B 官方解释instanceof判断A对象是不是B构造函数构造出来的,但真正是看A对象的原型链上有没有B的原型
区分一个变量是什么类型的3个方法
方法0
typeof
方法1
变量.constructor会返回他的构造函数
方法2
变量 instanceof 类型(Array Object)会返回 true false
方法3
调用toString方法,toString()方法是Object.prototype里面的一个方法,他的返回值也可以知道变量是什么类型
Object.prototype.toString.call({})
Object.prototype.toString.call([])
()运算符会把里面的东西变成表达式,用完一次直接会丢失索引
引用值比较的是地址,看是不是指向一个房间
实参列表arguments存储在AO里面,this : window也是存在AO里面
this
var a = 5;
function test(){
a = 0;
alert(a);
alert(this.a);
var a;//左右是把a提到AO里面,本来a是全局的本应该在GO里面
alert(a);
}
test();结果是0 5 0
new test();结果是 0 undefined 0
如果出现new test();this也发生着改变,系统内部会var this = {
__proto__ : test.prototype
}
1.函数预编译过程this指向window
2.全局作用域里面this指向window
3.call和apply可以改变函数的this指向
4.谁调用了该函数,this就指向谁,obj.a,obj调用函数a,this就指向obj
没人调用直接执行就是预编译 a.say代表函数体a.say()代表执行say方法
var name = "222";
var a = {
name : "111",
say : function(){
console.log(this.name);
}
}
var fun = a.say;
fun();
a.say();
var b = {
name : "333",
say : function (fun){
fun();
}
}
b.say(a.say);
b.say = a.say;
b.say();
arguments 上有length和callee属性
1.arguments.callee指向函数自身引用
var num = (function (n){
if(n == 1){
return 1;
}
return n * arguments.callee(n - 1);//求(n-1)的阶乘,但无函数名,所以只能借函数引用arguments.callee
}(20));
2.arguments.caller指代的是被调用的环境,返回被调用的环境,返回被谁调用,整个返回
function test(){
demo();
}
function demo(){
console.log(demo.caller);
}
属性有原始值和引用值之分,数组、对象是引用值
克隆
原始值拷贝是不出问题的,但如果里面有数组,数组是引用值,使用的是地址,指向的是同一个房间,就会导致其中一个改另一个就会跟着改 这种克隆叫浅层克隆
浅层克隆示例:
var obj = {
name : "abc",
age : 13,
sex : 'female'
}
var obj1 = {}
function clone(origin,target){
for(var prop in origin){
target[prop] = origin[prop];
}
return target;
}
clone(obj,obj1);
深度克隆 支持数组对象的克隆,克隆后不会因为一个改变而跟着改变,是独立分开的
function deepClone(origin,target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for(var prop in origin){
if(origin.hasOwnProperty(prop)){
if(origin[prop] !== 'null' && type(origin[prop]) == 'object'){
if(toStr.call(origin[prop]) == arrStr){
target[prop] = [];
}else{
target[prop] = {};
}
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
三目运算 条件判断?是:否 并且会返回值
if(toStr.call(origin[prop]) == arrStr){
target[prop] = [];
}else{
target[prop] = {};
}
变成三目运算
target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
字符串比较会比ascll码
变量定以后不给他赋值会提示undefined
数组 也是引用值 来源于Array.prototype 数据类型是对象 任意操作,基本不报错,只会undefined
var arr = []; 数组字面量[]里面传什么数据就是什么数据
var arr = new Array();构造函数定义数组,如果括号里面只传了一个参数他会默认是是数组长度,传1位必须是整数,长度没有小数,传小数报错
数组常用方法(es3.0) 数组自带方法 改变原数组
1.push往数组最后一位开始添加数据,可以同时多个数据添加
push方法
Array.prototype.push = function (){
for(var i = 0; i < arguments.length;i ++){
this[this.length] = arguments[i];
}
return this.length;
}
2.pop()把最后一位剪切出来,只能一位
3.shift在数组最前面删除数据
4.unshift在数组最前面添加数据
5.reverse把数组里面的数据逆转
6.splice(从第几位开始,截取多少的长度,在切口处添加新数据)切片,会返回切出来的
7.sort排序 按ascll码排序 数组里面如果是多位数字那就不能满足我们的排序 sort预留了一个接口给我们自己写方法实现
var arr = [1,2,5,12,23];
方法规则添加要求
1.必须写两个形参
2.看返回值 sort规则
1.当返回值为负数时,那么前面的数放在前面
2.为正数,那么后面的数放前面
3.为0,不动
var arr = [1,2,4,45,35,12,47]
方法会把前两位数据传进去,然后两个数进行比较,根据返回值排序所以直接a-b就是返回值
升序return a-b
降序return b-a
arr.sort(function(a,b){
if(a > b){
return 1;
}else{
return -1;
}
});
乱序
var arr = [1,2,5,12,23];
arr.sort(function(a,b){
return Math.random() - 0.5;
});
sort()接口原理,把前两个传进去,可以传数组对象,进去后看返回值,如果为正数,后面的放前面,如果为负数,前面的放前面
var deng = {
age : 19,
name : "niubi"
}
var cheng = {
age : 45,
name : "jicheng"
}
var zhang = {
name : "zhang",
age : 66
}
var arr = [deng,cheng,zhang];
arr.sort(function(a,b){
return a.age - b.age;
});
不改变原数组 那变量来接收
var arr = [1,2,3,4,5];
var arr1 = [6,7,8,9];
1.concat连接 arr.concat(arr1)
2.toString把数组变成字符串
3.slice(从该位开始截取,截取到该位)
slice(正数从该位开始截取到最后一位)
slice(负数截取倒数几位)
slice(不写)截取所有
4.join('字符串类型') 把数组里面的东西按指定符号连接起来返回一个字符串
5.split("按什么符号拆分") 按什么符号把字符串拆分成数组,
var str1 = 'alibaba';
var str2 = 'baidu';
var str3 = 'tencent';
var str4 = 'toudiao';
var arr = [str1,str2,str3,str4];
arr.join("");
类数组 如arguments 把数组和对象的特性都拼接到一起,使用更灵活,类数组必须要有length,属性要为数字索引,方法需要而外添加进去,如数组本应该有的方法push,splice方法都需要而外加进去他才有这个方法
类数组示例 特征要求:属性要为索引(数字)属性,必须要有length属性,最好加上push属性
var obj = {
"0" : 'a',
"1" : 'b',
"2" : 'c',
"length" : 3,
"push" : Array.prototype.push,
"splice" : Array.prototype.splice
}
1.可以利用属性名模拟数组特性
2.可以动态的增长length属性
3.如果强行让类数组调用push方法
数组push原理
Array.prototype.push = function(target){
this[this.length] = target;
this.length ++;
}
模拟typeof()方法
function type(target){
var template = {
"[object Array]" : "array",
"[object Object]" : "object",
"[object Number]" : "number - object",
"[object Boolean]" : "boolean - object",
"[object String]" : "string - object"
}
if(target === null){
return "null";
}else if(typeof(target) == "object"){
var str = Object.prototype.toString.call(target);
return template[str];
}else{
return typeof(target);
}
}
数组去重 借用对象里面属性唯一
var arr = [1,2,3,1,1,4,2,2];
Array.prototype.unique = function (){
var temp = {};
arr = [];
len = this.length;
for(var i = 0;i < len;i ++){
if(!temp[this[i]]){
temp[this[i]] = 'abc';//如果abc改成this[i]那就无法去掉0,因为重复0,!0是true
arr.push(this[i]);
}
}
return arr;
}
复习
包装类:
原始值包括正常的string number存储都在栈里面,没有属性,
但字符串是自带length属性的,
原始值字符串它隐式了new String(),这个隐式的过程叫包装类
原始值数字你给他加个属性他不会不错,原因是他也隐式了new Number().属性,但加完后他会给你删了,等于没执行,这个过程也叫作包装类
new一个构造函数返回值是对象
引用值 如数组,对象 ,函数,自带属性
原型
任何一个函数都会有一个prototype,prototype是这个函数的祖先
function Person(){
}
var person = new Person();
new Person()后他就会看自己有没有祖先上的属性,如果没有那就继承祖先的东,如果有就用自己的,没有就用祖先的
new Person()的时候隐式产生了
var this = {
__proto__ : Person.prototype
}
var obj = Object.create(原型)里面必须放原型(对象),然后在新建出的对象里面的原型指向demo即__proto__ : demo
var demo = {
lastName : "deng"
}
var obj = Object.create(demo);//即obj的原型指向demo
obj = {
__proto__ : demo
}
var num = 123;
一旦经过了var的操作,所得出的属性,window,这种属性叫做不可配置属性,不可配置的属性是不能delete的
delete只能删除可配置的属性,如obj.num = 123;是可以删除的
var obj = {}
obj.num = 123;
call/apply作用是改变this指向,区别是传参列表不一样
call/apply应用实例一
function Person(name,age){
this.name = name;
this.age = age;
}
function Student(name ,age, sex){
//var this = Object.create(Student.prototype);
Person.call(this,name,age);//使用Person()实现Student上的功能,就要把this指向Student
this.sex = sex;
}
call/apply应用实例二
function test(){
}
test() == test.call()在系统内部中执行test()的时候系统会自动变成test.call(),如果里面放参数,参数可以是对象,可以是window,this就会指向该参数,存于AO里面
this的4个特点
1.预编译过程this指向window
2.全局作用域里面this指向window
3.谁调用this指向谁
4.call和apply可以改变this指向
闭包
A函数里面有个B函数,通过各种手段把B保存到了A外部就形成了闭包,B就拿着A的AO和GO
闭包作用 私有化变量 除非自己说有,别人不可能知道他有
function Person(name){
var money = 100;
this.name = name;
this.makeMoney = function(){
money ++;
};
this.offer = function (){
money --;
}
this.say = function(){
console.log("I have money" + money);
}
}
var person = new Person();
[] + "" 会等于 ""
[] == [] 虽然名字相同但是两个房间,指向是不一样的
function (a){
var a;//形参相当于在函数里面var了一下a
}
表达式里面的函数名是无效的 表达式执行后就会丢失对函数名的索引
var h = function a(){
return 23;
}
表达式执行后就会对表达式里面的东西丢失索引
JavaScript中的数据类型分为原始值和引用值
原始值包括number string undefined boolean null symbol
引用值包括Array Object Function
作业
1.一个字符串a-z组成,找出该字符串第一个出现的,只出现一次的字母
2.字符串去重
try里面发生错误就会停止执行,错误前面的代码会正确执行,try外面的也会执行
catch的作用是捕捉错误里面只有两个参数name和message
try{
}catch(e){
e.message e.name
}
1.EvalError:eval()的使用与定义不一致
2.RangeError:数组越界
3.ReferenceError:非法或者不能识别的引用数值
4.SyntaxError:发生语法解析错误
5.TypeError:操作数类型错误
6.URIError:URI处理函数使用不当
es严格模式
目前浏览器都是运行基于es3.0 + es5.0的新增方法 目前如果es3.0和es5.0冲突了是按es3.0的标准执行,如果要用es5.0执行那就要开启es5.0
要启用es5.0就需要启用严格模式 "use strict"必须要把命令写在第一行,可以写在全局里面。那么整个页面都遵循es5.0,也可以写在局部函数里面,那么就这个函数遵循es5.0,推荐使用局部
很明显的是es5.0不支持arguments.callee方法
开启方式为什么不使用函数来开启,因为老版本的浏览器可能都没有更新到es5.0并没有这个函数,这样页面就直接报错了,而使用字符串在老版本浏览器上最多就是表达式没用,并不会报错
es5.0不能使用的语法
1.with(){}可以改变作用域链,他里面的语句会把AO指向with()指定的AO,with()里面放对象
var obj = {
name : "lisi"
}
var name = "zhangsan";
function test(){
var name = "wangwu";
with(obj){
console.log(name);
}
}
with的作用用处 简化代码 命名空间的正确使用需要配合with
var obj = {
dp1 : {
jc : {
name : "abc",
age : 123
},
deng : {
name : "nnn",
age : 111
}
},
dp2 : {
}
}
with(obj.dp1.jc){
console.log(name);
}
document是一个对象
with(document){
write('a');
}
with(){}把作用域链都改变了,所以es5.0不能使用的语法
2.arguments.callee,caller等一些方法在严格模式下不能使用了
3.严格模式下规定变量赋值前必须声明
4.严格模式下局部this必须要赋值,不赋值的话就是undefined,即在严格模式里局部的默认this是不指向window的
5.es5.0里面拒绝重复的参数和属性名 重复的参数会报错,重复的属性不报错
function test(name,name){
console.log(name);
}
test(1,3);
var obj = {
name : 123;
name : 11123;
}
6.eval()他可以执行代码 es3.0都规定不能使用 他会改变作用域链
什么DOM
1.DOM Document Object Model 文档对象模型
2.DOM定义了表示和修改文档所需的方法。用来操作HTML和xml功能的一类对象的集合,也有人称DOM是堆html和xml的标准编程接口,DOM不能操作css文件样式表,只是改的行间样式
xml----xhtml---HTML4.0--HTML5.0发展历程,现在xml都被json取代
var div = document.getElementsByTagName('div')[0]
div.style.height = "100px";
div.style.width = "100px";
div.style.backgroundColor = "red";
document代表整个文档,document上面有很多方法
var div = document.getElementById('');//id是唯一的
var div = document.getElementsByTagName('')[];//类数组,所有浏览器都可以使用
var div = document.getElementsByClassName('')[]//在老的浏览器不支持比如ie8以下
var div = document.getElementsByName()//理论上只有部分标签有效,如表单里面的、img
以下2个并不使用 不实时 是静态的 选完一次后就不变,是副本,新加的不会选进去 受局限
var div = document.querySelector('')//跟css一样选取,选的是一个,ie7以下没有
var div = document.querySelectorAll('')//跟css一样选取,选的是一组,放数组里面,ie7以下没有
css、html里面id用的少,大部分都使用class
遍历节点数 var div = document.getElementsByTagName('div')[0]
div.parentNode 父节点,最顶端的parentNode是document 每个元素都有这个属性
div.childNodes[i] 子节点们
div.firstChild 第一个子节点
div.lastChild 最后一个子节点
div.nextSibling 后一个兄弟节点
div.previousSibling 前一个兄弟节点
节点类型
元素节点---1 文本节点---3 注释节点---8 document---9
基于元素节点数的遍历 除了children其他的在IE9及IE9以下不兼容
parentElements 返回当前元素的父元素节点,是元素,document不是元素
children[i] 元素子节点 常用
firstElmentChild 第一个元素子节点
lastElementChild 最后一个元素子节点
node.childElementcount === node.children.length 当前元素子节点的数量
nextElementSibling 下一个兄弟元素节点
previousElementSibling 前一个兄弟元素节点
每一个节点都有的四个属性
nodeName 返回元素的标签名,只能读取不能修改
nodeValue 只有文本节点和注释有这个属性,可以修改和读取
nodeType 返回该节点的类型,只能读取,返回值是1.2.3.8.9
attributes 元素节点的属性集合,是个类数组
<div id="demo" class="demo1"></div>
div.attributes[0].nodeType返回值就是属性节点的返回值2
div.attributes[0].name
div.attributes[0].value
每个节点都有一个方法 hasChildNodes()方法判断是否有子节点
Document是一个系统留给自己的构造函数,他不能new
document是代表整个文本,继承自HTMLDocument,HTMLDocument继承自Document
HTMLDocument.prototype = {
__proto__ : Document.prototype
}
DOM结构树 表示的就是继承关系 最顶端是Node 最终还是继承自Object
Node--------Document--------HTMLDocument-----document
Node--------CharacterData--------Text/Comment
Node-------Element----------HTMLelement----HTMLHeadElement/HTMLBodyElement/HTMLTitleElement/...
Node----Attr
1.getElementById()是Document.prototype上的一个方法
2.getElementByName()是HTMLDocument.prototype上的方法
3.getElementsByTagName()是定义在Document.prototype和Element.prototype上的方法
var div = document.getElementByTagName('div')[0];
var span = div.getElementByTagName('span')[0];
var div = document.getElementByTagName('*')[0];//选中所有标签
4.HTMLDocument.prototype上定义了一些常用的属性,如body、head分别指代HTMLBodyElement、HTMLHeadElement
默认选好了body和head,直接使用document.body/document.head
5.document.documentElement----------->html
6.getElementsByClassName、querySelector、querySelectorAll在Document.prototype和Element.prototype上均有定义
DOM基本操作
1.增
创建一个元素节点,创建标签
var div = document.createElement('div');
document.body.appendChild(div);
div.innerHTML = 123;
创建文本节点
var text = document.createTextNode('邓哥');
创建注释节点
var comment = document.createComment();
2.插入 任何元素节点都有appendChild方法 插入到父元素里的最后面
PARENTNODE.appendChild
var div = document.getElementsByTagName('div');
var text = document.createTextNode('laodeng');
div.appendChild(text);把页面中已有的东西插入到另一个地方就是剪切操作,如果div里面之前有其他元素或者文本会保留,然后把新的插入到其后面
PARENTNODE.insertBefore
var div = document.getElementsByTagName('div');
var span = document.getElementsByTagName('span');
var strong = document.createElement('strong');
div.insertBefore(strong,span);
3.删除
parent.removeChild(); 做的是剪切操作
div.removeChild(i)
child.remove
i.remove(); 自尽,真销毁,没了
4.替换
parentNode.replaceChild(new,origin)拿新的替换老的,也是剪切
var strong = document.createElement('strong');
div.replaceChild(strong,p);
Element节点上的一些属性
1.innerHTML
div.innerHTML = '';//改变div里面的内容,他的作用是改变元素里面的内容,如果只元素里面有内容就会被覆盖,他还可以进行运算,比如保留之前的内容,然后把新内容追加放到后面,他取出来的是html标签,所以也可以写html标签代码进去div.innerHTML = "<p style='background: red;color: white;'>123</p>";双引号里面放单引号,不能双引号里面放双引号,会报错
2.innerText 火狐浏览器里面没有这个方法,但它提供了textContent跟这个方法一样,其他浏览器也有textContent,但IE不支持textContent
他会取他里面的所有文本
他还可以赋值,如果他里面还有结构,赋值后会覆盖之前的结构,变成赋值内容
Element节点上的一些方法
1.ele.setAttribute()给元素设置一个行间属性
ele.setAttribute('class','demo');
作用:可以先写好一个带class的样式,然后通过ele.setAttribute()给他加上,还可以通过if条件触发这个效果
<div></div>
<strong></strong>
<p></p>
给上面3个元素添加一个属性,属性名是this-name,值是他的标签名nodeName
var all =document.getElementsByTagName('*');
for(var i = 0;i < all.length;i ++){
all[i].setAttribute('this-name',all[i].nodeName);
}
2.ele.getAttribute()获取元素的一个属性,返回是属性的值
ele.getAttribute('id')
如果要修改一个元素的class和id可以直接赋值覆盖,
如div.className = '' div.id = ''
1.日期对象 系统提供好的 两个属性constructor,prototype
var date = new Date();
Date构造函数构造出的函数方法 基于那一时刻记录的时间
Date()返回当日的日期和时间
date.getDate()返回一个月的第几天
date.getDay()返回一周的第几天,从0开始,星期天开始0
date.getMonth()返回第几个月,从0开始,加1就OK
date.getFullYear()返回年份
date.getYear()有误会出现118,推荐使用date.getFullYear(),因为很早之前是6位时间,99.12.31出问题了
minutes、seconds、hours返回那一刻的时分秒
getTime()返回1970.01.01至今的的时间戳,毫秒数
var date = new Date();
date.setDate(15)设置date记录的是当月的第15天和那时刻的时分秒
设置一个时间,触发某代码
示例
var date = new Date();
date.setMinutes(40);
setInterval(function(new Date().getTime()-date.getTime() > 1000){
console.log('你很帅');
},1000);
date.setTime(45454464111),从1970.01.01开始毫秒
date.toString()把时间转换成字符串
计算机的纪元年是1970.01.01
js定时器
1.setInterval(代码,时间毫秒);循环执行里面的代码,只要没有清除,就不会停止,一直按时间循环,时间确定后永远不会变,就算后面改变也是无效的,定时器的精确度不是很准,setInterval()是window上的方法,每一个计时器都会有一个唯一标识,第一个计时器就是1,返回值是1
示例
setInterval(function(){},1000);
setInterval("console.log("a")",1000);也可以是双引号括起代码,一样意思,没人用
2.clearInterval();清除计时器 通过接受setInteval的唯一标识,是个数字从1开始,清除就是靠这个数字来进行的
示例
var timer = setInterval(function (){
console.log(i++);
if(i > 10){
clearInterval(timer);
}
},10)
3.var timer = setTimeout(function(){},1000);他的作用是在1000毫秒后执行,而且只执行一次
4.clearTimeout(timer),清除计时,直接不执行了
以上方法都是window上的方法,this都是指向window
BOM基本操作
1.查看滚动条观点距离
window.pageXOffset X轴移动距离,实际距离=首屏宽度+X,IE8及以下不支持
window.pageYOffset Y轴移动距离,实际距离=首屏高度+Y,IE8及以下不支持
IE8及以下使用的是 兼容性十分混乱 其中一个方法有效,另一个方法就是0
document.body.scrollLeft/Top IE8 IE5 IE4
document.documentElement.scrollLeft/Top IE7 IE6
所以解决兼容性的办法就是2个方法相加
document.body.scrollLeft + document.documentElement.scrollLeft
document.body.scrollTop + document.documentElement.scrollTop
封装一个完整的方法
function getScrollOffset{
if(window.pageXOffset){
return {
x : window.pageXOffset,
y : window.pageYOffset
}
}else{
return {
x : document.body.scrollLeft + document.documentElement.scrollLeft,
y : document.body.scrollTop + document.documentElement.scrollTop
}
}
}
2.查看视口尺寸 就是可视窗口大小
window.innerWidth/innerHeight返回值是像素,IE8及以下不兼容
IE8及以下使用的是
标准模式下任意浏览器可用方法
document.documentElement.clientWidth/clientHeight
怪异模式下的浏览器方法
document.body.clientWidth/clientHeight
浏览器有2种渲染模式:标准模式、怪异模式(混杂模式),渲染指的是把html翻译过来呈现出来,页面最前有DTD文档类型声明就是标准模式,如果没有就会启动混杂模式
compat--兼容性
封装一个完整的方法
function getViewportOffset(){
if(window.innerWidth){
return {
w : window.innerWidth,
h : window.innerHeigth
}
}else{
if(document.compatMode === 'BackCompat'){
return {
w : document.body.clientWidth,
h : document.body.clientHeight
}else{
return {
w : document.documentElement.clientWidth,
h : document.documentElement.clientHeight
}
}
}
}
}
3.查看元素尺寸和位置 方法一和二求的都是元素可视的大小,包括padding
方法一 不常用
1.es5.0的新方法,返回结果不是实时的,而是求的那一时刻的静态写照,后期改变大小或者位置,他求的是不会改变的,静态保存,不会动态更新
2.位置求的是左上顶点和右下位置的顶点坐标
3.ele.getBoundingClientRact();//结果里面一共6个数据,top、bottom、left、right、元素height、width,此方法在IE里面求出来只有4个参数,没有height和width,间接使用right-left和bottom-top可以求到
方法二 常用方便 任何一个元素都可以使用
1.查看元素尺寸的方法ele.offsetWidth ele.offsetHeight此方法求的是元素实际可视的大小,包括padding
2.查看元素位置ele.offsetLeft/Top/Right/Bottom 求的是相对于父级的位置,前提是有父级定位,如果没有父级定位求的是相对文档的距离
注意:横向margin会相加,纵向的margin会塌陷重合到padding或者内容区域里面,position的默认值是static
ele.offsetParent求的是返回有定位的父级的坐标位置,如果没有有定位的父级就返回body
4.让滚动条滚动
window.scroll(X,Y) window.scrollTo(X,Y) 两个方法都是一样的,作用是使滚动条滚动到要求的位置,不能多次累加
window.scrollBy(X,Y) 此方法可以多次累加
脚本化CSS 通过DOM间接操作css
1.ele.style所有元素都有这个方法,他会返回一个样式表,类数组,可以读写里面的数据,所以就可以直接修改,而且是实时更新,只有这一种方法可以读取和写入操作的方法,只此一种,他操作的是行间样式,不在行间就看不到,所有浏览器都支持
ele.style.width = "";修改值必须是字符串,
ele.style.backgroundColor = "";//用-组合的属性,js里面没有-符号,用小驼峰代替
ele.style.cssFloat = "left";//float比较特殊,他是保留字,所以w3c推荐改成cssFloat,但float本身也可以
ele.style.border = ""//
2.window.getComputedStyle(ele,null); 计算而来 没有相对值,只有绝对值10em=160px
1.此方法根据样式的权重获取,谁的权重高就获取谁的,得到的是最终的结果,他只可以查询,不能修改
2.IE8及以下不兼容,他使用ele.currentStyle.width
3.null是用来获取伪元素的属性
封装兼容方法
function getStyle(ele,prop){
if(window.getComputedStyle){
return window.getComputedStyle(ele,null)[prop];
}else{
return ele.currentStyle[prop];
}
}
3.变成思想
想改变一个特殊的元素的css,可以给他再次创建一个class样式,到时候把改该元素的class改成新创建的class,把值赋给他就可以达到实现,直接要修改的操作先预先写好css然后改className
比如
.yellow::after{
content : "";
width : 10px;
height : 10px;
background : yellow;
display : inline-block;
}
.green::after{
content : "";
width : 10px;
height : 10px;
background : green;
display : inline-block;
}
4.position的left和top默认值是auto,是个相对值
事件 交互触发效果
绑定事件处理函数
1.ele.onclick = function(){} 此方法兼容性很好,但是一个元素同一事件只能绑定一个,相当于属性赋值,如果再绑定一个就会覆盖之前的,此方法就相当于写在行间了,也可以直接写在行间,onclick = "代码"
2.ele.addEventListener(type,function,false)此方法IE9以下不兼容,可以绑定多个事件处理函数,如果处理函数相同那么只执行一次,按事件绑定顺序执行
3.IE独有 ele.attachEvent('onclick',function(){});此方法也是可以绑定多个处理函数,绑定几次就执行几次,可以绑定相同的函数
注意:绑定事件的时候如果用了for循环,就要考虑闭包了,特别是循环给一个元素添加事件的时候,因为一直用着i
示例
<script>
var li = document.getElementsByTagName('li');
var div = document.getElementsByTagName('div')[0];
div.addEventListener('click',function(){
alert('牛逼');
},false);
for(var i = 0;i < li.length;i ++){
li[i].addEventListener('click',function(){
console.log(i);
},false);
}
</script>
解决办法就是使用加一个立即函数
事件的this:指代的都是哪个元素绑定的事件,this就指向哪个元素,ele.onclick ele.addEventListener(type,function,false)都是指向ele,
但IE的ele.attachEvent('onclick',function(){});指向是window,
要让他指向ele的示例
div.attachEvent('onclick',function(){
hander.call(div);
});
function hander(){
// 事件处理
}
封装事件绑定兼容性方法
function addEvent(ele,type,handle){
if(ele.addEventListener){
ele.addEventListener(type,handle,false);
}else if(ele.attachEvent){
ele.attachEvent('on'+type,function(){
handle.call(ele);
});
}else{
ele['on'+click] = handle;
}
}
解除事件 不需要要这个事件了
1.ele.onclick = null;
2.ele.removeEventListener('click',原函数引用,所以当时必须把事件处理函数拿到外面写,留下引用接口,不然无法解除,false);//写函数名即可,如果写test()会立即执行,写索引就可以了
3.ele.detachEvent('on'+type,原函数引用,所以当时必须把事件处理函数拿到外面写,留下引用接口,不然无法解除);//写函数名即可,如果写test()会立即执行,写索引就可以了
事件处理2个模型 如果两个都存在先事件捕获后事件冒泡
focus、blur、change、submit、reset、select等事件不冒泡
1.事件冒泡 代码结构存在父子关系就会存在事件冒泡即同一事件,自子元素冒泡向父元素
<div class="wrapper">
<div class="content">
<div class="box"></div>
</div>
</div>
自box---content---wrapper
<script>
var wrapper = document.getElementsByTagName('div')[0];
var content = document.getElementsByTagName('div')[1];
var box = document.getElementsByTagName('div')[2];
wrapper.addEventListener('click',function(){
console.log('wrapper');
},false);
content.addEventListener('click',function(){
console.log('content');
},false);
box.addEventListener('click',function(){
alert('box');
},false);
</script>
注意:事件冒泡是存在代码结构上的,不是视觉效果上的
2.事件捕获 只有Chrome一款浏览器有 从外面开始 自父元素捕获至子元素 最新款浏览器基本都有但IE没有,触发顺序,先捕获后冒泡
<div class="wrapper">
<div class="content">
<div class="box"></div>
</div>
</div>
自wrapper---content---box
<script>
var wrapper = document.getElementsByTagName('div')[0];
var content = document.getElementsByTagName('div')[1];
var box = document.getElementsByTagName('div')[2];
wrapper.addEventListener('click',function(){
console.log('wrapper');
},true);
content.addEventListener('click',function(){
console.log('content');
},true);
box.addEventListener('click',function(){
alert('box');
},true);
</script>
注意:IE上特有的事件获取,也叫事件捕获,通过ele.setCapture()把所有事件都捕获到自己身上,要释放的时候使用ele.releaseCapture()
3.取消冒泡和阻止默认事件
1.事件对象,点击事件函数里面可以放一个形参,系统会把事件对象的所有参数数据传到里面,包括很多当前点击的很多数据
2.w3c标准取消冒泡方法event.stopPropagation();但IE9以下不支持
示例
div.onclick = function(e){
e.stopPropagation();
this.style.background = "green";
}
3.IE独有方法是event.cancelBubble = true;
封装取消冒泡方法
function stopBubble(){
if(event.stopPropagation){
event.stopPropagation();
}
else{
event.cancelBubble = true;
}
}
4.阻止默认事件 表单提交 a标签跳转 右键菜单等
1.阻止鼠标右键
document.oncontextmenu = function(e){
console.log('a');
return false;//句柄的方式
e.preventDefault();//常用
}
2.取消a标签默认事件在行间写<a href="javascript:void()"></a>此a标签就是去了跳转功能
封装一个方法解决阻止默认事件的方法
function cancelHandler(e){
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
1.事件对象 event || window.event用于IE
2.事件源对象 触发事件的源头
求事件源对象:event.target 火狐有这个
event.srcElement IE有这个
Chrome浏览器两个方法都有
封装兼容性方法
ele.onclick = function(e){
var event = e || window.event;
var target = event.target || event.srcElement;
console.log(target);
}
3.事件委托 利用事件冒泡和事件源对象进行处理 本来是儿子完成的任务委托给父亲 子元素冒泡给父元素
<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>
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e){
var event = e || window.event;
var target = event.target || event.srcElement;
console.log(target.innerText);
}
事件委托优点
1.性能,不需要使用循环,使用循环还有闭包问题,如果循环1亿次呢
2.灵活,当有新的子元素进来时不需要重新绑定事件
鼠标事件
click = mousedown + mouseup
contextmenu鼠标右键
mousemove鼠标移动
mouseover==mouseenter鼠标移上去
mouseout==mouseleave鼠标移开
监控鼠标左键右键 mousedown和mouseup可以监测左右键,其他的不行,onclick监测的是左键
如果一个操作既要实现拖拽又要跳转,实现方法是根据时间差来实现
<script>
var firstTime = 0;
var lastTime = 0;
var key = false;
document.onmousedown = function(){
firstTime = new Date().getTime();
}
document.onmouseup = function(){
lastTime = new Date().getTime();
if(lastTime - firstTime < 300){
key = true;
}
}
document.onclick = function (){
if (key) {
console.log('click');
key = false;
}
}
</script>
1.鼠标右键返回值是2,左键是0
<script>
document.onmousedown = function(e){
if(e.button == 2){
console.log('right');
}else if(e.button == 0){
console.log('left');
}
}
</script>
2.移动端里面的3个事件是
touchstart
touchmove
touchend
键盘事件
1.onkeydown 按下
2.onkeyup 松手
3.onkeypress 持续按不松手keydown>keypress
触发顺序 keydown>keypress>keyup
keydown keypress区别
keydown可以监测到所有按键,但charCode是0不能判断是哪个按键
keypress只能监测字符类按键,ASCLL码里面有的字符,charCode会返回ASCLL值
操作类按键上下左右keydown的which返回值会不一样,他监测的是键盘的排序
文本操作事件
1.input事件,文本框里面的内容发生变化就会触发该事件
var input = document.getElementsByTagName('input')[0];
input.oninput = function(e){
console.log(this.value);
}
2.change事件,文本框获得焦点和失去焦点前后是否有变化,有变化就触发,没有就不触发
var input = document.getElementsByTagName('input')[0];
input.onchange = function(e){
console.log(this.value);
}
3.onfocus获得焦点
4.onblur失去焦点
滚动条滚动是window上的事件
window.scrollX
window.scrollY
window.scrollTo(X,Y)
IE里面没有position:fixed;那就可以使用js给他利用position:absolute给他做出来实现,滚动条滚动了多少就把滚动的距离加到top上
onload事件也是window上的,页面加载完成后执行触发js,页面所有事情都完成后才执行,包括图片视频样式等所有页面东西,这就会导致效率低速度慢,可以放到容易地方
每个html文档解析的时候会产生domTree和cssTree,两棵树拼到一起就是一棵渲染树,创建domTree的时候只要确认他是什么标签就把他挂到树上
json
JSON是一个静态类,系统提供,类似Math
最早的传输数据格式是xml
JSON是一种传输数据的格式,他就是一个对象,换了个词就叫json
为了跟对象区别,属性名必须加""
示例
var obj = {
"name" : 'deng',
"age" : 123
}
数据传输的过程是把字符串传过去,那就需要把json换成字符串的格式来传输
把json对象换成字符串系统提供的方法JSON.stringify(obj);
var str = JSON.stringify(obj);
后台给前台传输数据也是传输过来的字符串,把字符串转换成对象的方法是JSON.parse(str)