2020-09-02

JavaScript高级程序设计

一、基础

1.1 基础语法

  1. 区分大小写:一切都区分大小写
  2. 标识符:变量、函数、属性的名字或函数的参数。组成,字母、数字、下划线和美元符号,第一个字符不能是数字。不可把关键字、保留字、true等用作标识符。
  3. 注释:单行注释用“//”,多行注释用“/**/”。
    CSS注释:“/* */”
    Html注释:“<!-- -->”
  4. 严格模式:整个脚本启用,在顶部添加“use strict”。函数启用,在函数内部的上方添加。在严格模式下,不确定的行为会得到处理,对某些不安全的操作会抛出错误。
  5. 语句:以一个分号结尾,可省略,但建议不省略。
    在这里插入图片描述

1.2 概念

  • 变量、命名、声明、赋值(你可以把任何东西存储在变量里,如数值、字符串、布尔值等)、表达式、自增、比较操作符、逻辑与或非
  • 操作符优先级:算数操作符>比较操作符>逻辑操作符>赋值符号“=”;
  • ++i和i++:前者为i先自增1再干别的事,后者为i先干事再自增1。例如i=5; var a=(i++)+(i++); var b=(++i)+(++i);输出a=11 b=13;
  • 逻辑运算符&&和位运算符&;
1.2.1 变量
  • 用var定义。ECMAScript的变量是松散类型的,松散类型就是可以用来保存任何数据。
  • 函数体内省略var创建的变量是一个全局变量,但不推荐这样创建全局变量。
  • 一条语句定义多个变量:
var age = 24, 
	sex = '男', 
	found = false;
1.2.2 数据类型
  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
  • 引用数据类型:对象(Object)、数组(Array)、函数(Function)、、、、、、、、。
typeof null;    //返回object,typeof是操作符不是函数,所以可以跟括号但不必须。
1.2.2.1 undefined与null

undefined:声明变量但未初始化的值默认为undefined。

var ageWang;
alert(ageWang);    //"undefined"
alert(ageZang);    //出错,ageZang is not defined
alert(typeof ageZang);    //ageZang虽然未声明,但输出类型为undefined。[疑惑][疑惑][疑惑]

null:表示一个空对象指针。typeof(var car = null)返回‘object”
如果定义的变量准备在将来用于保存对象,那么最好将变量初始化为null。

if (car != null){
	//对car对象执行某些操作
}

null和undefined区别:undefined 表示一个变量自然的、最原始的状态值,null则表示一个变量被人为的设置为空对象Symbol 是ES6 引入了一种新的原始数据类型,表示独一无二的值。没必要把一个变量的值显示地定义为undefined,但只要意在保存对象的变量还没有保存对象,就应该明确地让变量保存为null。

1.2.2.2 Boolean

Boolean类型的值:true、false。True和False不是它的值,只是标识符。
在这里插入图片描述

1.2.2.3 Number

八进制,第一位是零0
十六进制,前两位是0x

  1. 浮点数:
var floatNum = .1;   // 有效但不推荐,0.1。
var floatNum2 = 1.;    // 解析为整数,1
var floatNum3 = 10.0;    //解析为整数,10

e表示法(科学计数法):值为e前面的数值乘以10的指数次幂。例如3e-7表示0.00000003
浮点数值的最高精度为17位小数,例如:

console.log(0.1 + 0.2); // 结果是0.30000000000000004

永远不要测试某个特定的浮点数值。if(a + b =0.3); //不要做这样的测试。
2. 数值范围
能表示的最小数是:5e-324,做大数是1.7976931348623157e+308。不在范围内的会被自动转化为-Infinity(负无穷)或Infinity(正无穷)。Infinity不能参与计算。判断一个数值是否有穷用isFinite()函数。
3. NaN
即非数值(Not a Number),用于表示一个本来返回数值的操作数未返回数值的情况(这样就不会抛出错误啦)。例如,在其他编程语言中任何数除以零都会导致错误,但在ECMAScript中,任何数(除零外,0/0得到NaN)除以零会返回Infinity,不影响其他代码的执行。
NaN的特点:任何涉及NaN的操作(例如NaN/10)都会返回NaN;NaN与任何值都不相等,包括NaN本身。
针对NaN的这两个特点,ECMAScript定义了 isNaN()函数。这个函数接受一个参数,该参数可以 是任何类型,而函数会帮我们确定这个参数是否“不是数值”。isNaN( )在接收到一个值之后,会尝试 将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串-10 •或Boolean值。而任何不能被转换为数值的值都会导致这个函数返回true。请看下面的例子:

alert(isNaN(NaN));    //true
alert(isNaN(10));   //false
alert (isNaN("10"));     //false
alert (isNaN("blue"));    //true,字符串"blue"不能被转换成数值
alert(isNaN(true>);    //false
  1. 数值转换
    有三个函数可以把非数值转换为数值:Number()、parselnt ()和parseFloat ()。Number ()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值,更常用。
    Number(null); //null转换为数字为0
    Number(undefined); //undefined转换为数字为NaN
var num1 = parseInt("1234blue123");    //1234,遇到非数字字符停止解析
var num2 = parseInt("");    //NaN,如果第一个字符不是数字字符或者负号,返回NaN
var num3 = parseInt("OxA");    //10,以“0x”开头且后跟数字字符,就会将其当作一个十六进制整数
var num4 = parseInt(22.5);    //22,小数点并不是有效的数字字符,遇到非数字字符停止解析
var num5 = parseInt("070");    //70(ES5+);56(ES3,按八进制解析)
var num6 = parseInt("70");    //70
var num7 = parseInt("10",	8);	   //8,指定基数为8按八进制解析
var num8 = parseInt("10",	10);   //10,指定基数是10按十进制解析 

不指定基数意味着让parselnt ()决定如何解析输入的字符串,因此为了避免错误的解析,我们建 议无论在什么情况下都明确指定基数。多数精况下,我们要解析的都是十进制数值,因此始终将10作为第二个参数是 非常必要的。parseFloat()只解析十进制值,因此它没有用第二个参数指定基数的用法

var numl = parseFloat("1234blue");	//1234 (整敎)
var num5 = parseFloat("0908.5");	//908.5,始终忽略前边的0
var num2 = parseFloat("0xA");	//0,十六进制的就会转化为0,因为始终忽略前边的0
var num3 = parseFloat("22.5");	//22.5
var num4 = parseFloat("22.34.5");	//22.34,解析到遇见第一个无效浮点数字字符为止,也就是只有第一个小数点是有效的
var num6 = parseFloat("3.125e7n);	//31250000
1.2.2.4 String

转换为字符串:
方法1,toString()方法。数值、布尔值、对象和字符串值(没错,每个字符申也都有一个toString()方法,该方法返回字符串的一个副本)都冇toString()方法。但null和undefined值没有这个方法。默认情况下toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格式表示的字符串值。例如:var num = 11; alert(num.toString(8));
方法2,在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String ()。这个 函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:如果值有toString ()方法,则调用该方法(没有参数)并返回相应的结果;如果值是null,则返冋“null”;如果值是 undefined,则返回"undefined”。

var num;
var num2 = null;
// alert(num2.toString())  //出错,null没有toString()方法
alert(String(num));
1.2.2.5 Symbol

表示独一无二的值。
Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

let age = Symbol('wang'),
    age1 = Symbol('zhang');
console.log(age == age1); //加参数是为了区分两个变量,如果不加参数或加一样的参数两个变量的输出虽是一样的但不相等,所以函数的参数只是对当前symbol值得描述

"your symbol is " + age;  // Symbol 值不能与其他类型的值进行运算。TypeError: can't convert symbol to string
String(age);  // ‘Symbol(wang)’,但可以转化为字符串,或用方法toString()
Boolean(age);  //也可以转为布尔类型,但不能转为数值。

1.3 操作符

  • 一元操作符、位操作符、布尔操作符、乘除余、加减、相等、关系、条件
  • 自增,自减操作符应用于非数字字符时,先转换为数字字符再运算。应用于对象时,先调用对象的valueOf()方法取得一个值,再进行运算。如果结果是NaN,则在调用toString()方法后再运算。
  • 布尔操作符:逻辑与&&、或||、非!
    1. 逻辑非可用于将一个值转换为其对应的布尔值,用 “!!”
    2. 或||,如果两个数都是 null(undefined/NaN), 则返回null(undefined/NaN)。
    3. 与&&。如果有一个操作数是 null(undefined/NaN), 则返回null(undefined/NaN)。
      总结:逻辑运算符, || 和 && 都是遵行短路原则,只要确定符号前面的真假,既可确定返回值。需要说明的是 && 的优先级是高于 || 的。
      在这里插入图片描述
// ==运算符,如果有一个是布尔值,则比较之前将其转为数值;
//如果一个是字符串一个是数值,比较之前先将字符串转化为数值;
!!"false" == !!"true" // true
// ==> true == true
// ==> true

['x'] == 'x' // true
// == 运算符对数组类型执行 number 转换,先调用对象的 valueOf() 方法,结果是数组本身,不是原始类型值,所以执行对象的 toString() 方法,得到字符串 'x'

[1,2,3] == [1,2,3]  // false
// 当运算符两边类型相同时,不会执行类型转换,两个数组的内存地址不一样,所以返回 false
// 条件操作符
// var max = (num1 > num2) ? num1 : num2; 如果num1 > num2将num1赋值给max,否则将num2赋值给max
var go = function(a) {
    var str = 'go';
    var add0 = function(a) {
        str += 'o';
        return a ? str += a : add0; // 巧妙使用
    }
    return a ? str += a : add0; // 巧妙使用
}
console.log(go('l')); //gol
console.log(go()('l')); //gool
console.log(go()()('l')); //goool

1.4 语句

3.1 if语句
    if小写,大写字母(IF)会出错!
3.2 switch语句
    当有很多种选项的时候,switch比if else使用更方便。
    语法:
        switch(表达式)
        {
        case值1:
          执行代码块 1
          break;
        case值2:
          执行代码块 2
          break;
        ...
        case值n:
          执行代码块 n
          break;
        default:
          与 case值1 、 case值2...case值n 不同时执行的代码
        }
    注意: 记得在case所执行的语句后添加上一个break语句。否则就直接继续执行下面的case中的语句。
3.3 for循环
        for (num=1;num<=6;num++)  //初始化值;循环条件;循环后条件值更新
        {   document.write("取出第"+num+"个球<br />"); }
3.4 while循环
        while(判断条件)
        {   循环语句    }
3.5 Do...while循环
        do
        {
            循环语句
         }
        while(判断条件)
3.6 退出循环break、继续循环continue
    在while、for、do...while、while循环中使用break语句退出当前循环,直接执行后面的代码;
    continue的作用是仅仅跳过本次循环,而整个循环体继续执行。
3.7 fon-in语句
用于循环对象的属性,循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。
    for (var propName in window) {    //ECMAScript对象的属性没有顺序。因此,通过for-in循环输出的属性名的顺序是不可预测的。
		document.write(propName);    //每次执行循环时,都会将window对象中存在的一个属性名赋值给变最propName。
	}

如果表示要迭代的对象的变量值为null或undefined,这种情况不执行循环体。为了保证最大限度的兼容性,建议在使用for-in循环之前,先检测确认该对象的值不是null或undefined。
3.8 with语句
将代码的作用域设置到一个特定的对象中。
定义with语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

上面几行代码都包含location对象。如果使用with语句,可以把上面的代码改写成如下所示:

with(location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

严格模式下不允许使用with语句,否则将视为语法错误。由于大量使用with语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with语句。

3.9 lable语句
下面例子中定义的start标签可以在将来由break或continue语句引用。加标签的语句一般都 要与for语句等循环语句配合使用。
     begin: for (var i = 0; i < 10 ; i++ ){
       alert(i);
     }

添加lable前:

var num = 0;
for (var i = 0; i < 10; i++) {
    for (var j = 0; j < 10; j++) {
        if (i == 5 && j == 5) {
            break;    //若break改为continue,则当i为5,j为5时跳过当前这次循环中continue后面还没有执行的语句,接着进行下一次循环,即j++后,j=6的循环。
        }
        num++;
    }
}
alert(num); // 循环在 i 为5,j 为5的时候跳出 j循环,但会继续执行 i 循环,输出 95

添加lable后:

var num = 0;
outPoint:
    for (var i = 0; i < 10; i++) {
        for (var j = 0; j < 10; j++) {
            if (i == 5 && j == 5) {
                break outPoint; //若break改为continue,在i为5,j为5时跳出当前j循环,并跳转到outPoint(标签)下的i循环继续执行,num最终等于95。
            }
            num++;
        }
    }
alert(num); // 循环在 i 为5,j 为5的时候跳出双循环,返回到outPoint层继续执行,输出 55

1.5 函数

4.1 语法
  • 第一种:函数声明:function foo(){}

  • 第二种:使用Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。
    var double = new Function(‘x’, ‘return 2 * x;’); //注意末尾有个分号

  • 第三种:函数表达式:var foo = function name{}
    JS函数可以通过一个表达式定义。函数表达式可以存储在变量中,一旦函数被存储在变量中,这个变量就可以被用来调用这个函数。因为这已经变成了一个表达式,函数名称name只是函数体中的一个本地变量,所以在函数外部无法通过函数名称name访问到函数。

    例:var f = function g(){ return 12; }
         1. typeof f,结果是function     //f是一个方法
         2. typeof f(),结果是number    //调用f方法返回数字12给f
         3. typeof g,结果是undefined.   //函数表达式,g只是作为当前函数的一个内部属性。
         4. typeof g(),结果是Error     //会先调用方法g,但不能在外部调用所以报错。
    
4.2 返回值的函数
	位于return语句之后的任何代码 都永远不会执行。
	return语句也可以不带有任何返回值(return;)。在这种情况下,函数在停止执行后将返回undefined 值。
4.3 理解参数

ECMAScript函数不介意传递进 来多少个参数,也不在乎传进来参数是什么数据类型:

function howManyArgs() {
alert(arguments.length)howManyArgs("string", 45)//2
howManyArgs();	//0
howManyArgs(12);	//1

arguments对象可以与命名参数一起使用,num1的值与arguments[0]的值相同:

function doAdd(num1, num2) {
    if (arguments.length == 1) {    // 通过判断参数长度来执行不同的操作,模仿重载。
        alert(num1 + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + num2);
    }
}
doAdd(10, 20);

arguments的值永远与对应命名参数的值保持同步:每次执行次函数都会重写第二个参数,将第二个参数修改为10,修改了 arguments[1] 也就修改了num2,但修改命名参数不会改变arguments中的值;他们的内存空间是独立的;如果只传入一个参数,那么 arguments[1] 设置的值不会反应到命名参数中;没有传递值得命名参数将自动被赋予undefined值。

function doAdd(num1, num2) {
    arguments[1] = 10;
    alert(arguments[0] + num2);
}
doAdd(20, 30)  //30
doAdd(20)  //NaN,arguments对象的长度是由传入参数的个数决定的,不是由定义函数时的命名参数的个数决定的。

严格模式下,即使把arguments[1] 设置为10,num2的值由传入的参数决定。重写arguments的值会导致语法错误(我试了,执行了,貌似没有语法错误!)

4.4 没有重载

函数是对象,函数名是指针。把函数名想象为指针,实际上在创建第二个函数时,覆盖了引用第一个函数的变量。
可以通过判断参数长度来执行不同的操作,模仿重载。

注:变量与函数重名时,变量生效。
- 变量声明会被顶置,函数声明也会被顶置且比变量更先声明。
- 变量的声明和赋值语句一起写时,JS引擎在解析时,会将其拆成声明和赋值2部分,声明置顶,赋值保留在原来位置。
alert(sum(10,10));
function sum(num1, num2){
	return num1 + num2;
}

//alert(sun(10,10));
//var sum = function(num1, num2){
//	return num1 + num2;
//}

被注释的代码运行会出错,因为函数位于一个初始化语句中,而不是一个函数声明。

4.5 一个函数中返回另一个函数
// 根据某个对象属性对数组进行排序
function compare(pn) {
    return function(o1, o2) {
        //return value1 - value2;
        var value1 = o1[pn];
        var value2 = o2[pn];
        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else { return 0; }
    }
}
var person = [{ name: 'wang', age: 20 }, { name: 'zhang', age: 18 }, { name: 'yang', age: 19 }]; // 创建包含两个对象的数组
person.sort(compare('name')); // name 加引号与不加引号的区别
console.log(person);
4.6 函数内部属性

arguments

阶乘。将return num*factorial(num-1);改为return num*arguments.callee(num-1);但现在已经不推荐使用。
原因:访问 arguments 是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建。影响现代浏览器的性能,还会影响闭包。
替代方案:
function factorial(num) {
  if (num <= 1) {
    return 1
  }
  var fac = 1
  return (function fn() {
    fac *= num
    num--
    if (num != 0) {
      fn()
    }
    return fac
  })()
}
factorial(5) // 120

this

指向
4.7 函数的属性和方法

每个函数都包含两个属性:length和prototype
length:接收的参数的个数
prototype:

方法:apply()、call()、bind()作用都是在特定作用域中调用函数。
apply():第一个参数是运行函数的作用域,第二个是参数数组或arguments
call():第一个参数是运行函数的作用域,第二个是逐个列举出来的参数
bind():会创建一个函数的实例

window.color = 'red';
var a = { color: 'green' }

function showColor() {
    return this.color;
}

console.log(showColor()); //red
console.log(showColor.call(this)); //red
console.log(showColor.call(window)); //red
console.log(showColor.call(a)); //green
var b = showColor.bind(a);
console.log(b()); //green

1.6 引用数据类型

1.6.1 Object类型

JavaScript 提供多个内建对象, 比如 String、 Date、 Array 等等。 对象只是带有属性和方法的特殊数据类型。
1、 创建对象的方法:
方法一:使用Object:

// 1、 new Object([value]);     //value可以是任何值  
var obj = new Object("text");    // 类似于obj = {0:"t", 1:"e", 2:"x", 3:"t"};
console.log(obj.length);    //4,就相当于一个字符串了吗竟有length属性?????????????????????

var person = new Object();
person.firstname = "John";
person.lastname = "Doe";     //创建了对象的一个新实例,并添加了两属性,一个方法
person.eat = function() {}
// 2、字面量形式,对象字面量表示是对象定义的一种简写形式
var person = {
    firstName: "John",
    lastName: "Doe",     // 在使用对象字面量语法时,属性名也可以使用字符串:'lastName': "Doe"
    age: 50,
    methodName: function() {}
};
// var person = {};    //只包含默认属性和方法,与new Object()相同

方法二:使用对象构造器,自定义对象:

// 构造函数:
function person(firstname, lastname, age, eyecolor) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
    this.eyecolor = eyecolor;
    this.changeName = changeName;

    function changeName(name) {
        this.lastname = name; //changeName()函数name的值赋给person的lastname属性
    }
}

var myFather = new person("John", "Doe", 50, "blue");    // 创建对象实例: 一旦有了对象构造器, 就可以创建新的对象实例
// 在一个已存在的对象构造器中是不能添加新的属性的, 要添加一个新的属性需要在构造器函数中添加。或使用 prototype 属性就给对象的构造函数添加新的属性。

通常使用构造器(函数体)定义属性,使用原型对象(prototype)定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,使代码更具可读性:见2.1prototype

2、 访问对象的方法:
访问对象属性: person.lastName; 或方括号表示法person[“lastName”];(除非必要,否则使用点表示法)
访问对象方法: objectName.methodName()

//如果属性名中包含会导致语法错误的字符或属性名用的是关键字保留字,用点表示法会出错。
var person = {
    'last Name': "Doe",
    '*123': '456'
    'add0': 'bj',
    'add1': 'tj',
    'add2': 'sjz'
};
console.log(person["*123"]);    // 注意加引号

var addr = "";
for (var i = 0; i < 3; i++) {
    addr = person['add' + i];    // 读取person对象的add0,add1,add2属性
    console.log(addr);
}

3、for…in语句
用于循环遍历对象的属性

for (variable in object)
{
    代码... //for...in循环中的代码块将针对每个属性执行一次  
}
1.6.2 Array类型
  • 2.1 数组创建

     var myarray = new Array();
     var myarray = new Array(8); //创建长度为8的数组
     var myarray = new Array(66,80,90,77,59);//创建数组同时赋值
     var myarray = [66,80,90,77,59];//直接输入一个数组(称 “字面量数组”)
     myarray[5]=88;    //向数组中添加一个新元素
     注意:
         1.创建的新数组是空数组,没有值,如输出则显示undefined。
         2.虽然创建数组时,指定了长度,但实际上数组都是变长的,也就是说即使指定了长度为8,仍然可以将元素存储在规定长度以外。
         3.数组存储的每一项数据可以是任何类型(数字、字符、布尔值等)
    
  • 2.2 数组属性length

      Length属性表示数组的长度,即数组中元素的个数。
      特别注意:
           1. JavaScript数组的length属性是可变的。数组随元素的增加,长度也会改变。
           2. 通过在设置length,可以向数组中添加新项或从数组的末尾移除项。
    
var colors = ['red', 'blue', 'green'];
colors.length = 2;
alert(colors[2]);
colors[colors.length] = 'black';    // 利用length在末尾添加
colors[colors.length] = 'brown';
colors[99] = "black";    // 在99的位置添加新项,这时数组长度是100,而位置4-98没有存值会返回undefined
  • 2.3 相关方法
    在这里插入图片描述

  • 2.4 二维数组

      1. 二维数组的定义方法一
         var myarr=new Array();  //先声明一维 
         for(var i=0;i<2;i++){   //一维长度为2
            myarr[i]=new Array();  //再声明二维 
            for(var j=0;j<3;j++){   //二维长度为3
            myarr[i][j]=i+j;   // 赋值,每个数组元素的值为i+j
            }
          }             
      2. 二维数组的定义方法二
         var Myarr = [[0 , 1 , 2 ],[1 , 2 , 3]]
    
1.6.3 Regular Expression
语法:/正则表达式主体/修饰符
主体在两个斜杠中间,紧接着是修饰符,修饰符是可选的。

修饰符:
    i:不区分大小写;
    g:全局匹配,而非在找到第一个匹配之后停止;
    m:多行匹配;

RegExp的方法:只有三个test(string)、exec()、compile()(1.51版本中已废弃)、toString
支持正则表达式的String方法: 
search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
replace() 方法 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
match(),找到内容若干个。
split(),把字符串分割为字符串数组。

1.7 变量、作用域、内存问题

在这里插入图片描述
在这里插入图片描述

1.8 垃圾回收、内存泄漏

  • 什么是垃圾?
    在JavaScript中,如果一个对象不再被引用,就会被回收;如果两个对象相互引用,而不再被第三者引用,那么这两个相互引用的对象也会被回收。
    1、全局变量不会被回收
    2、只要被另一个作用域所引用就不会被回收
    3、局部变量会被回收,也就是函数一旦执行完以后,函数内部的东西会被销毁。

  • 什么是内存泄漏?
    不再用到的内存,没有及时释放,就叫内存泄漏(memory leak)。有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。

  • 大多数语言提供自动内存管理,这被称为“垃圾回收机制”。

  • 并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值,一旦不再用到,你必须检查是否还存在对它们的引用。如果是的话,就必须手动解除引用。

let arr = [1, 2, 3, 4];
console.log('hello world');
arr = null;    // 解除arr对[1, 2, 3, 4]引用,这块内存就可以被垃圾回收机制释放了。
// 数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。

什么情况会引起内存泄漏?
原链接1
原链接2
虽然有垃圾回收机制但是我们编写代码操作不当还是会造成内存泄漏。

  1. 全局变量
function colors() {
    first = 'blue';    // 调用函数后,first将成为一个全局变量,不会被回收
}

原因:全局变量,不会被回收。
解决:使用严格模式。

  1. 闭包
    注意!
    注意!
    注意!闭包不会引起内存泄漏。
    闭包造成的内存泄漏是旧版本IE的bug,真正情况下的闭包不会造成内存泄漏。

  2. 没有清理的DOM元素引用

var refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, "refA");  // 但是还存在引用,能console出整个div 没有被回收

原因:虽然别的地方删除了,但是对象中还存在对dom的引用
解决:手动删除refA = null;。
注意: 此外还要考虑 DOM 树内部或子节点的引用问题。假如你的 JavaScript 代码中保存了表格某一个 的引用。将来决定删除整个表格的时候,直觉认为 GC 会回收除了已保存的 以外的其它节点。实际情况并非如此:此 是表格的子节点,子元素与父元素是引用关系。由于代码保留了 的引用,导致整个表格仍待在内存中。保存 DOM 元素引用的时候,要小心谨慎。

  1. 被遗忘的定时器或者回调函数
var someResouce=getData();
setInterval(function(){
    var node=document.getElementById('Node');
    if(node){
        node.innerHTML=JSON.stringify(someResouce)
        // 定时器没有清除
    }
    // node、someResource 存储了大量数据,无法回收
},1000)

原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。
解决:在定时器完成工作的时候,手动清除定时器。

内存泄漏的识别方法?
经验法则是,如果连续5次垃圾回收后,内存占用一次比一次大,就有内存泄漏。这就要求实时查看内存占用。

  1. 浏览器
    Chrome浏览器查看内存占用步骤:

  2. 命令行
    命令行可以使用Node提供的process.memoryUsage方法

console.log(process.memoryUsage());
// { rss: 27709440,
//  heapTotal: 5685248,
//  heapUsed: 3449392,
//  external: 8772 }

在这里插入图片描述

process.memoryUsage返回一个对象,包含了 Node 进程的内存占用信息。该对象包含四个字段。判断内存泄漏,以heapUsed字段为准。

怎样避免内存泄漏
1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
2)注意程序逻辑,避免“死循环”之类的 ;
3)避免创建过多的对象 原则:不用了的东西要及时归还。

二、面向对象

2.1 prototype原型对象
  1. 万物皆对象,对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。
  2. 方法(Function)是个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。Date 对象从 Date.prototype 继承。Array 对象从 Array.prototype 继承。所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
我们知道,在一个已存在的对象构造器中是不能添加新的属性的。有时候我们想要在对象的构造函数中添加属性或方法。使用 prototype 属性就可以给对象的构造函数添加新的属性和方法:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
}

                                Person.prototype.nationality = "English";
                                Person.prototype.name = function(){
                                    return this.firstName + " " + this.lastName;
                                };
通常使用构造器(函数体)定义属性,使用原型对象(prototype)定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,使代码更具可读性:
                                // 构造器内定义属性
                                function Fun(a, b) {
                                  this.a = a;
                                  this.b = b;
                                }

                                // 原型属性定义方法
                                Fun.prototype.c = function() {
                                  return this.a + this.b;
                                }
2.2 继承

阮一峰的日志:
参考2:
方式一:原型链继承
原型链是实现继承的最原始模式,即通过prototype属性实现继承。

function Father() { this.fatherProp = true } //父级-构造函数
Father.prototype.getFatherValue = function() { return this.fatherProp } //父级-原型方法
function Son() { this.sonProp = false } //子级-构造函数

Son.prototype = new Father() //继承,它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。
Son.prototype.constructor = Son;  // 将Son.prototype对象的constructor值改为Son。

Son.prototype.getSonValue = function() { return this.sonProp } //子级-添加原型方法
var son = new Son() //创建子级的实例对象
console.log(son.getFatherValue()) //true
解析:son实例对象是如何找到getFatherValue()方法的呢?
首先在son对象自身中找。若对象自身没找到,然后在Son.prototype中找。若Son.prototype中没找到,继续往上一层,Son.prototype.__proto__(Fater.prototype),依次类推,直到找到需要的属性或方法,或到达原型链顶端Object.prototype

注意:通过原型链实现继承后,不能再使用字面量的方式创建原型对象,因为会覆盖原型链。
//子级-原型属性:继承父级
Son.prototype = new Father()
//不能像下面这样,这样会使得上面的代码无效
//因为这相当于重新创建了一个原型对象
Son.prototype = {
getSonValue: function() {
return this.sonProp
}
}
缺点:
a、无法实现继承多个
b、来自原型对象的所有属性被所有实例共享,会相互影响。
c、创建子类实例时,无法向父类构造函数传参

方式二:借用构造函数
方式一的缺点b可借用构造函数的方式解决。核心思想:在子级构造函数中调用父级构造函数。如何实现在一个构造函数中调用另一个函数?——call()和apply()。

function Father() {
    this.arr = [1, 2, 3]
}
Father.prototype.getName = function() {
    return true
}

function Son() {
    //call的第一个参数是this指向的对象,即构造函数的实例对象
    Father.call(this);  // 将父对象的构造函数绑定在子对象上
}

var son1 = new Son()
console.log(son1.arr) //1,2,3

var son2 = new Son()
son2.arr.push(4)

console.log(son2.arr) //1,2,3,4
console.log(son1.arr) //1,2,3
son2.getName() // 报错,找不到getName()

缺点:只能继承到父级构造函数的属性和方法,却不能继承父类原型上的属性和方法。
特点:可以实现多继承(call多个父类对象)。解决了1中,子类实例共享父类引用属性的问题;

方式三:组合继承
组合继承 = 原型链 + 借用构造函数。取长补短:共享的用原型链,各自的借用构造函数
function Father(name) {
this.name = name
this.arr = [1,2,3]
}
Father.prototype.getName = function() {
console.log(this.name)
}

                    function Son(name, age) {
                       //构造函数继承
                       Father.call(this, name) 
                       this.age = age
                    }
                    //原型链继承
                    Son.prototype = new Father()
                    Son.prototype.constructor = Son

                    var son1 = new Son("小名", 23)
                    son1.arr.push(4)
                    console.log(son1.arr) //1,2,3,4
                    son1.getName()    //小名

                    var son2 = new Son("一灯", 24)
                    console.log(son2.arr) //1,2,3
                    son1.getName()    //一灯

    解析:
        既解决了共享问题,又解决了父级原型属性方法不能继承问题。
    缺点:
        每次创建子类实例都执行了两次构造函数(Father.call()和new Father()),虽然这并不影响对父类的继承,但子类创建实例时,原型中会存在两份相同的属性和方法,这并不优雅。

方式四:寄生组合继承
为了解决构造函数被执行两次的问题, 我们将指向父类实例改为指向父类原型, 减去一次构造函数的执行。

function Father(name) {
    this.name = name
    this.arr = [1, 2, 3]
}
Father.prototype.getName = function() {
    console.log(this.name)
}

function Son(name, age) {
    //构造函数继承.把子类的this对象传到Father的方法里面,然后把父类的属性绑定到子类的this 上
    Father.call(this, name)
    this.age = age
}
//原型链继承
//Son.prototype = new Father()       //指向父类实例
Son.prototype = Object.create(Father.prototype) //将`指向父类实例`改为`指向父类原型`
Son.prototype.constructor = Son

var son1 = new Son("小名", 23)
son1.getName() //小名

特点:基本上完美,实现起来比较复杂。

	Object.create(object[, propertiesObject])内部原理:
	function object(o) {
			function F() { }    // 创建空对象
			F.prototype = o;    // 指定空对象的原型为参数o
			return new F();
	}

// 利用空对象作为中介,弥补直接继承prototype(Cat.prototype = Animal.prototype;)
function extend (Child, Parent) {
  var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype; //为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、“上一层”。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
  }
  extend(Cat,Animal);
  只用直接继承prototype或者利用空对象作为中介缺点:
  与构造函数相反:不能继承构造函数里的属性方法,只能继承父类原型上的属性和方法。

方式五、 拷贝继承
缺点:与直接继承(Cat.prototype = Animal.prototype;)一样,不能继承构造函数里的属性方法

// 把父对象的所有属性和方法,拷贝进子对象
function Animal() { this.name = 'maomao' }
Animal.prototype.species = "动物";

function Cat() {}

function extend2(Child, Parent) {    
    var p = Parent.prototype;    
    var c = Child.prototype;    
    for (var i in p) {  c[i] = p[i];   }    
    c.uber = p;  
}
extend2(Cat, Animal); 
var cat1 = new Cat("大毛", "黄色"); 
alert(cat1.species); // 动物

方式六:
“非构造函数"的继承:两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现"继承”。

// 非构造函数继承,object()方法
var Chinese = {
    guoji: 'china',
    color: 'yellow'
}
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
var Doctor = object(Chinese);
Doctor.name = 'doctor';   // 不能用字面量的方式加子对象属性,要放在上一步后面
console.log(Doctor.name);

方式七:class - - ES6
语法糖,底层任然是原型链继承的方式
//class 相当于es5中构造函数
//class中定义方法,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的,这一点与 ES5 的行为不一致。Object.keys(类名)
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式,类不存在变量提升
//es5中constructor为隐式属性
// constructor方法是类的默认方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class People{
constructor(name=‘wang’,age=‘27’){
this.name = name;
this.age = age;
}
eat(){
console.log(${this.name} ${this.age} eat food)
}
}

                //继承父类
                class Woman extends People{ 
                   constructor(name = 'ren',age = '27'){ 
                     //继承父类属性
                     super(name, age); 
                   } 
                    eat(){ 
                     //继承父类方法
                      super.eat() 
                    } 
                } 
                let wonmanObj=new Woman('xiaoxiami'); 
                wonmanObj.eat();
typeof Point // "function",类的数据类型是函数,但类必须使用new调用,否则会报错。
Point === Point.prototype.constructor // true,类本身就指向构造函数constructor,这与 ES5 的行为是一致的;
                                     // 类的所有方法都定义在累的prototype属性上面,在类的实例上面调用方法,其实就是调用原型上的方法。
  • 作为函数时,super()只能用在子类的构造函数constructor之中,用在其他地方就会报错。
  • super作为对象时,在普通方法中,指向父类的原型对象。所以定义在父类实例上(constructor中的)的属性和方法,是无法通过super调用的。
  • super作为对象时,在静态方法中,指向父类。

三、匿名函数

  • 匿名函数就是没有函数名的函数。匿名函数是通过函数表达式而不是函数声明定义的。
  • 匿名函数最大的用途是创建闭包,并且可以构建命名空间,以减少全局变量的使用。
    1、var foo = function (){} //定义匿名函数
    function(){ return’Lee’; } //报错,匿名函数不能通过函数声明的方式定义。单独的匿名函数是无法运行的。
    2、自调用匿名函数:
    (function(x) {
    alert(x+10);
    })(3); //调用之后返回13
    解释:第一对括号向脚本返回未命名的函数,然后在匿名函数后面接一对括号 (),调用这个匿名函数。
    用途:可以用它创建命名空间,只要把自己所有的代码都写在这个特殊的函数包装内,那么外部就不能访问,除非你允许;
    在实际项目开发中,我们在一个页面中可能会同时引入多个Javascript代码库,如果每个代码库中的函数都是有名函数,则可能会产生命名冲突。所以现在很多流行框架都使用自调用匿名函数来解决以上问题。

四、闭包

  • 闭包就是能读取其他函数内部变量的函数。
  • 创建闭包常见方式:在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。
  • 匿名函数和闭包之间没有什么关系,只不过很多时候在用到匿名函数解决问题的时候恰好形成了一个闭包。
    function checkClosure(){
    var str = ‘rain-man’;
    setTimeout(
    function(){ alert(str); } //这是一个匿名函数,setTimeout调用它2秒后。
    , 2000);
    }
    checkClosure();
    知识点:checkClosure函数的执行是瞬间的(也许用时只是0.00001毫秒),在checkClosure的函数体内创建了一个变量str,在checkClosure执行完毕之后str并没有被释放,这是因为setTimeout内的匿名函数存在这对str的引用。待到2秒后函数体内的匿名函数被执行完毕,str才被释放。
    var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
    })();
    add();
    add();
    add();
    // 计数器为 3
    自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。
    闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。

用处:可以读取函数内部的变量;可以让这些变量的值始终保持在内存中。
使用闭包的注意点:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包;解决方法是,在退出函数之前,将不使用的局部变量全部删除
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

var name = "The Window";

var object = {    
    name: "My Object",
    getNameFunc: function() {  
        // var obj = this; 
        return function() {        
            return this.name;      
        };    
    }  
}; 
alert(object.getNameFunc()());  // The Window
// 闭包绑定自由变量是不能使用this的,毕竟this是完全指向环境对象的。如果你想达成闭包的效果,你就需要在闭包的外部函数getNameFunc中指向object

五、事件响应,交互

5.1 什么是事件
    JavaScript 创建动态页面。
    事件是可以被 JavaScript 侦测到的行为。 网页中的每个元素都可以产生某些可以触发 JavaScript 函数或程序的事件。
    注意: 在网页中,如使用事件,就在该元素中设置事件属性。如:<input name="button" type="button" value="点击提交" onclick="add2()" />

5.2 鼠标单击事件onclick
    通常与按钮一起使用。比如,我们单击按钮时,触发 onclick 事件,并调用两个数和的函数add2()。

5.3 鼠标经过事件onmouseover
    当鼠标移到一个对象上时,该对象就触发onmouseover事件,并执行onmouseover事件调用的程序。

5.4 鼠标移开事件onmouseout
    当鼠标移开当前对象时,执行onmouseout调用的程序。

5.5 光标聚焦事件onfous
    当网页中的对象获得聚点时,执行onfocus调用的程序就会被执行。

5.6 失焦事件onblur
    onblur事件与onfocus是相对事件,当光标离开当前获得聚焦对象的时候,触发onblur事件,同时执行被调用的程序。
5.7 文本框内容改变事件onchange
    通过改变文本框的内容来触发onchange事件,同时执行被调用的程序。

5.8 加载事件onload
    事件会在页面加载完成后,立即发生,同时执行被调用的程序。
    注意:1. 加载页面时,触发onload事件,事件写在<body>标签内。
        2. 此节的加载页面,可理解为打开一个新页面时。

5.8 卸载事件onunload
    当用户退出页面时(页面关闭、页面刷新等),触发onUnload事件,同时执行被调用的程序。
    注意:不同浏览器对onunload事件支持不同。

六、JavaScript内置对象

6.1 什么是对象?
    JavaScript 中的所有事物都是对象,如:字符串、数值、数组、函数等,每个对象带有属性和方法。JavaScript 提供多个内建对象,比如 String、Date、Array 等等,使用对象前先定义。
    
6.2 日期对象Data()
    日期对象可以储存任意一个日期,并且可以精确到毫秒数(1/1000 秒)。
    定义一个时间对象 : var Udate=new Date();   //使 Udate 成为日期对象,并且已有初始值:当前时间(当前电脑系统时间)。
    注意:使用关键字new,且Date()的首字母必须大写。
    6.2.1 返回/设置年份方法
        get/setFullYear() 返回/设置年份,用四位数表示。
                    var mydate=new Date();//当前时间2014年3月6日
                    document.write(mydate+"<br>");//输出当前时间
                    document.write(mydate.getFullYear()+"<br>");//输出当前年份
                    mydate.setFullYear(81); //设置年份
                    document.write(mydate+"<br>"); //输出年份被设定为 0081年。
        注意: 结果格式依次为:星期、月、日、年、时、分、秒、时区。(Google浏览器);不同浏览器,时间格式有差异。
        改了年份 星期会改变??????????
    6.2.2 返回星期方法
        getDay() 返回星期,返回的是0-6的数字,0 表示星期天。如果要返回相对应“星期”,通过数组完成,代码如下:
                    <script type="text/javascript">
                      var mydate=new Date();//定义日期对象
                      var weekday=["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
                    //定义数组对象,给每个数组项赋值
                      var mynum=mydate.getDay();//返回值存储在变量mynum中
                      document.write(mydate.getDay());//输出getDay()获取值
                      document.write("今天是:"+ weekday[mynum]);//输出星期几
                    </script>
    6.2.3 返回/设置时间方法
        get/setTime() 返回/设置时间,单位毫秒数(一秒 1000 毫秒)。
                    var mydate=new Date();
                    mydate.setTime(mydate.getTime() + 60 * 60 * 1000);
                    document.write("推迟一小时时间:" + mydate);
6.3 字符串对象String
    定义字符串的方法就是直接赋值。比如:var mystr = "I love JavaScript!"
    toUpperCase(): 将字符串小写字母转换为大写。
    
    6.3.1 charAt()方法
        返回指定位置的字符。返回的字符是长度为 1 的字符串。
        语法: stringObject.charAt(index)      
        参数:index表示字符串中某个位置的数字,即字符在字符串中的下标。
        注意:1.字符串中第一个字符的下标是 0。最后一个字符的下标为字符串长度减一(stringObject.length-1)。2.如果参数 index 不在 0 与 stringObject.length-1 之间,该方法将返回一个空字符串。3. 一个空格也算一个字符。
    
    6.3.2 indexOf() 方法
        返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回 -1。
        语法: stringObject.indexOf(substring, startpos)
        参数:substring,规定需检索的字符串值。
            startpos,可选的整数参数。规定在字符串中开始检索的位置。它的合法取值为 stringObject.length-1。如省略该参数则从字符串的首字符开始检索。
    6.3.3 split()
        将字符串分割为字符串数组,并返回此数组。
        语法:stringObject.split(separator,limit)
        参数:separator,从该参数指定的地方分割stringObject;
            limit,可选参数,分割的次数,如果设置该参数,返回的子串不会多于这个参数指定的数组,如果无此参数则不限制次数。
        注意:如果把空字符串 ("") 用作 separator,那么 stringObject 中的每个字符之间都会被分割。
    6.3.4 substring()
        substring() 方法用于提取字符串中介于两个指定下标之间的字符。
        语法: stringObject.substring(startPos,stopPos) 
        参数:startPos,一个非负整数,开始位置
            stopPos,可选参数,结束位置,如果省略该参数那么返回的子串会一直到字符串对象的结尾。
        注意:
            1. 返回的内容是从 start开始(包含start位置的字符)到 stop-1 处的所有字符,其长度为 stop 减start。
            2. 如果参数 start 与 stop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。
            3. 如果 start 比 stop 大,那么该方法在提取子串之前会先交换这两个参数。
    6.3.5 substr()
        substr() 方法从字符串中提取从 startPos位置开始的指定数目的字符串。
        语法: stringObject.substr(startPos,length)
        参数:startPos,要提取的子串的起始位置。必须是数值。
            length,可选参数,提取字符串的长度,如果省略该参数那么返回的子串会一直到字符串对象的结尾。
        注意:如果参数startPos是负数,从字符串的尾部开始算起的位置。即,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推(相当于从字符串 startPos + length 的位置开始截取,也就是先将负数加长度化为正数再提取);
            如果startPos为负数且绝对值大于字符串长度,startPos为0。

6.4 Math对象
    Math对象,提供对数据的数学计算。
    注意:Math 对象是一个固有的对象,无需创建它,直接把 Math 作为对象使用就可以调用其所有属性和方法。这是它与Date,String对象的区别。例如:var mypi=Math.PI;  //PI属性是圆周率π
    常用方法:
        ceil floor round random
6.5 Array数组对象
    数组对象是一个对象的集合,里边的对象可以是不同类型的!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!。
    数组定义/数组属性:见,二、数组 2.1数组创建
    
    常用方法:
       1、concat(array1,array2,...,arrayN):连接。
			2、join(分隔符):把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。
			3、reverse()颠倒数组中元素的顺序。
			4、slice(start,end):从已有的数组中返回选定的元素。String.slice() 与 Array.slice() 相似。
			5、sort(方法函数):使数组中的元素按照一定的顺序排列。
           数组元素为字母时,方法函数可以省略,按Unicode位自动排序从小到大。
           数组元素位数字时,升序排序 return  a - b; 降序排序 return b - a。
       6、push()方法:可向数组的末尾添加一个或多个元素,并返回新的长度。shift()是删掉数组最后一个元素。
       7、unshift()方法:在数组起始位置添加元素请使用 unshift() 方法。pop()是删掉数组第一个元素。
       8、map(function(currentValue,index,arr), thisValue)方法:返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

七、浏览器对象

BOM浏览器对象模型:window对象、history对象、location对象、navigator对象、screen对象
7.1 windows对象
    window对象是BOM的核心,window对象指当前的浏览器窗口。
    7.1.1 JavaScript计时器
        在JavaScript中,我们可以在设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。
        计时器类型:一次性计时器:仅在指定的延迟时间之后触发一次;    间隔性触发计时器:每隔一定的时间间隔就触发一次。
        计时器方法:
        1、setInterval():从载入页面后每隔指定的时间执行代码。
            语法: setInterval(代码,交互时间);
            参数说明:1. 代码:要调用的函数或要执行的代码串。 
                   2. 交互时间:周期性执行或调用表达式之间的时间间隔,以毫秒计(1s=1000ms)。
            返回值: 一个可以传递给 clearInterval() 从而取消对"代码"的周期性执行的值。
        2、clearInterval():取消由 setInterval() 设置的交互时间。
            语法: clearInterval(id_of_setInterval)
            参数说明:  id_of_setInterval:由 setInterval() 返回的 ID 值。
        3、setTimeout()
            setTimeout()计时器,在载入后延迟指定时间后,去执行一次表达式,仅执行一次。
            语法: setTimeout(代码,延迟时间);
            参数说明:1. 要调用的函数或要执行的代码串。
                   2. 延时时间:在执行代码前需等待的时间,以毫秒为单位(1s=1000ms)。
        4、clearTimeout()
            setTimeout()和clearTimeout()一起使用,停止计时器。
            语法: clearTimeout(id_of_setTimeout)
            参数说明: id_of_setTimeout:由 setTimeout() 返回的 ID 值。该值标识要取消的延迟执行代码块。
    7.1.2 history对象
        记录了用户曾经浏览过的页面(URL),并可以实现浏览器前进与后退相似导航的功能。
        注意:从窗口被打开的那一刻开始记录,每个浏览器窗口、每个标签页乃至每个框架,都有自己的history对象与window对象关联。
        语法:window.history.[属性|方法],window可以省略。
        属性:length,返回浏览器历史列表中的URL数量。
        方法:back(),加载history列表中的前一个URL。等同点击浏览器的倒退按钮。back()相当于go(-1)
            forward(),加载history列表中的下一个URL。forward()相当于go(1)
            go(),加载history列表中某个具体页面。
        1、back()
        2、forward()
        3、go()
            go()方法,根据当前所处的页面,加载 history 列表中的某个具体的页面。
            语法: window.history.go(number);
            参数:
    7.1.3 location对象
        用于获取或设置窗体的URL,并且可以解析URL。
        语法: location.[属性|方法]
        location对象属性图示:
        location对象属性:
        location对象方法:
    7.1.4 navigator对象
        包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本。
        属性:
    7.1.5 screen对象
        用于获取用户的屏幕信息。
        语法: window.screen.属性
        1、屏幕分辨率的高和宽
            a. screen.height 返回屏幕分辨率的高
            b. screen.width 返回屏幕分辨率的宽
            注意: 1.单位以像素计。 2. window.screen 对象在编写时可以不使用 window 这个前缀。
        2、屏幕可用高和宽
            a. screen.availWidth 属性返回访问者屏幕的宽度,以像素计,减去界面特性,比如任务栏。
            b. screen.availHeight 属性返回访问者屏幕的高度,以像素计,减去界面特性,比如任务栏。
            注意: 不同系统的任务栏默认高度不一样,及任务栏的位置可在屏幕上下左右任何位置,所以有可能可用宽度和高度不一样。

八、DOM对象

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。DOM将HTML文档呈现为带有元素、属性和文本的树状结构(节点树)。DOM可以使JavaScript 获得了足够的能力来创建动态的 HTML。
HTML文档可以说由节点构成的集合,DOM节点有:
    1、元素节点:<html>、<body>、<p>等标签都是元素节点。
    2、文本节点:向用户展示的内容,如<li>...</li>中的JavaScript、DOM、CSS等文本。
    3、属性节点:元素属性,如<a>标签的链接属性href="http://www.imooc.com"。

8.1 获取元素节点及元素节点属性的方法
    1、getElementById()方法
    2、getElementsByName()方法
        返回带有指定名称的节点对象的集合。
        语法:document.getElementsByName(name)
        与getElementById() 方法不同的是,通过元素的name属性查询元素,而不是id属性。
        注意:1.因为文档中的name属性可能不唯一,所有getElementsByName()方法返回的是元素的数组,而不是一个元素。
            2.和数组类似也有length属性,可以和访问数组一样的方法来访问,从0开始。
    3、getElementsByTagName()方法
        返回带有指定标签名的节点对象的集合。返回元素的顺序是它们在文档中的顺序。
        语法:document.getElementsByTagName(Tagname)
        说明:1.Tagname是标签的名称,如p、a、img等标签名。
            2.和数组类似也有length属性,可以和访问数组一样的方法来访问,所以从0开始。
    三种方法的区别:
<img src="img\获得HTML元素方法的区别.png" title="获得HTML元素方法的区别">

    4、getAttribute()方法
        通过元素节点的属性名称获取属性的值。
        语法: elementNode.getAttribute(name)
        说明: elementNode是使用getElementById()、getElementsByTagName()等方法获取到的元素节点;name是想查询的元素节点的属性名称。
    5、setAttribute()方法
        把指定的属性设置为指定的值。如果不存在具有指定名称的属性,该方法将创建一个新属性。
        语法: elementNode.setAttribute(name,value)
        说明: elementNode是使用getElementById()等方法获取到的元素节点;name是要设置的属性名,value是要设置的属性值;
        
8.2 节点属性
    在DOM中每个节点都是一个对象。DOM节点的三个重要属性:
    1、nodeName属性
        节点的名称,是只读的。
        a. 元素节点的 nodeName 与标签名相同
        c. 属性节点的 nodeName 是属性的名称
        c. 文本节点的 nodeName 永远是 #text
        d. 文档节点的 nodeName 永远是 #document
    2、nodeType属性
        节点的类型,是只读的。以下常用的几种结点类型:

                            元素类型    节点类型
                              元素          1
                              属性          2
                              文本          3
                              注释          8
                              文档          9
    3、nodeValue属性
        节点的值
        a. 元素节点的 nodeValue 是 undefined 或 null
        b. 文本节点的 nodeValue 是文本自身
        c. 属性节点的 nodeValue 是属性的值
        
8.3 遍历节点树
    1、childNodes访问子节点
        访问选定元素节点下的所有子节点的列表,返回的值可以看作一个数组,它具有length属性。如果选定的节点没有子节点,则该属性返回不包含节点的NoodeList。
        语法:elementNode.childNodes
    2、访问子节点的第一项和最后一项
        firstChild属性返回子节点数组的第一个子节点。如果选定的节点没有子节点,则返回NULL。与elementNode.childNodes[0]是同样的效果。
        lastChild属性返回子节点数组的最后一个子节点。如果选定的节点没有子节点,则返回NULL。
        注意:我们知道Internet Explorer 会忽略节点之间生成的空白文本节点,而其它浏览器不会。我们可以通过检测节点类型,过滤子节点。
    3、parentNode访问父节点
        获取指定节点的父节点。父节点只能有一个。访问祖节点: elementNode.parentNode.parentNode
        语法:elementNode.parentNode
    4、访问兄弟节点
        1、nextSibling 属性可返回某个节点之后紧跟的节点(处于同一树层级中)。
        2. previousSibling 属性可返回某个节点之前紧跟的节点(处于同一树层级中)。
        注意: 两个属性获取的是节点。Internet Explorer 会忽略节点间生成的空白文本节点(例如,换行符号),而其它浏览器不会忽略。
                              <body>
                                <ul id="u1">   
                                            <li id="a">javascript</li>   
                                            <li id="b">jquery</li>   
                                            <li id="c">html</li>   
                                        </ul>   
                                        <ul id="u2">   
                                            <li id="d">css3</li>   
                                            <li id="e">php</li>   
                                            <li id="f">java</li>   
                                        </ul>   
                                <script type="text/javascript">
                                    function get_nextSibling(n){
                                        var x=n.nextSibling;
                                        //x用来检测当前结点是不是空的,x.nodeType!=1 检测该节点是不是元素节点
                                        while (x && x.nodeType!=1){
                                            x=x.nextSibling;
                                        }
                                        return x;
                                    }

                                    var x=document.getElementsByTagName("li")[0];
                                    document.write(x.nodeName);
                                    document.write(" = ");
                                    document.write(x.innerHTML);

                                    var y=get_nextSibling(x);
                                    //判断是不是最后一个节点
                                    if(y!=null){
                                        document.write("<br />nextsibling: ");
                                        document.write(y.nodeName);
                                        document.write(" = ");
                                        document.write(y.innerHTML);
                                    }else{
                                      document.write("<br>已经是最后一个节点");      
                                    }

                                </script>
                              </body>
    
8.4 节点方法:
    1、appendChild(newnode)在指定节点的最后一个子节点列表之后添加一个新的子节点。
    2、insertBefore(newnode,node)在已有的子节点前插入一个新的子节点。
    3、removeChild(node)删除节点。    把删除的子节点赋值给 x,这个子节点不在DOM树中,但是还存在内存中,可通过 x 操作。
    4、replaceChild(newnode,oldnode)替换元素节点。    当 oldnode 被替换时,所有与之相关的属性内容都将被移除。
    5、creatElement()创建元素节点:document.createElement(tagName)
    6、createTextNode()创建文本节点
8.5  
    1、浏览器窗口可视区域大小
        var w= document.documentElement.clientWidth || document.body.clientWidth;
        var h= document.documentElement.clientHeight || document.body.clientHeight;
        Document对象的body属性对应HTML文档的<body>标签。
    2、scrollHeight和scrollWidth,获取网页内容高度和宽度???????????????????????????
        var w=document.documentElement.scrollWidth || document.body.scrollWidth;
        var h=document.documentElement.scrollHeight || document.body.scrollHeight;
    3、offsetHeight和offsetWidth,获取网页内容高度和宽度(包括滚动条等边线,会随窗口的显示大小改变)。
        offsetHeight = clientHeight + 滚动条 + 边框。????????????????????????????????
        var w= document.documentElement.offsetWidth || document.body.offsetWidth;
        var h= document.documentElement.offsetHeight || document.body.offsetHeight;
    4、网页卷去的距离与偏移量
        scrollLeft:设置或获取位于给定对象左边界与窗口中目前可见内容的最左端之间的距离 ,即左边灰色的内容。
        scrollTop:设置或获取位于对象最顶端与窗口中可见内容的最顶端之间的距离 ,即上边灰色的内容。
        offsetLeft:获取指定对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置 。
        offsetTop:获取指定对象相对于版面或由 offsetParent 属性指定的父坐标的计算顶端位置 。
        注意: offsetParent:布局中设置postion属性(Relative、Absolute、fixed)的父容器,从最近的父节点开始,一层层向上找,直到HTML的body。

JS ajax jQuery json

Ajax 技术提供了一种新的前后端数据交互方式,不需要刷新页面,异步的去请求去获取、交互数据。
JSON是JavaScript内置的对象表示法,目的是为了让Ajax技术中的数据传输更方便更简单。

可以用以下JSON形式表示李念这个对象,可以把它赋给一个变量,然后来使用任意属性。
{
“Name”:“李念”,
“Stature”:“163cm”,
“Still”:“https://cdn.yinshua86.com/study/linian.jpg”
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值