渡一js视频笔记

1. 浏览器

1.1 语言类型

  • 编译性语言 c c++

    • 优点: 快, 写完后才整体翻译成一个文件
    • 缺点: 不跨平台
  • 解释性语言 javascript php

    • 优点: 跨平台,不需要编译成文件,写一行编译一行;单线程

    • 缺点: 稍慢

.java — javac — 编译 — .class — jvm虚拟机 — 解释执行

java不属于两种语言中的任一种,是 oak 语言

1.2 主流浏览器及内核

IE trident

chrome webkit/blink

firefox gecko

opera presto

safari webkit

1.3 值类型 — 数据类型

  • 不可改变的原始值(栈数据 先进后出)
    • number、string、boolean、undefined、null
  • 引用值(堆数据)
    • array、object、function、…date、RegExp
var arr = [1, 2];
var arr1 = arr;
arr = [1, 3]; // arr重新指向一个新的堆地址,但是arr1的地址仍是原来的 
document.write(arr1); // [1, 2]
document.write(arr); // [1, 3]
var a = 10;
var b = ++a - 1 + a++; // 11 - 1 + 11  a后加变成12
// b = 21		a = 12

var a = 1;
var b = a-- + --a; // 0
// 先算--a,此时a=0,然后a-- + 0 = 0,之后a = -1

var b = --a + --a; // -1
// b = 0 + -1
// a = 1-1 = 0,然后--a ===> a = -1
1 / 0 = Infinity(是一个number)
0 / 0 = NaN(Not a Number)

转化为boolean为false的值:undefined、null、NaN、""(空字符串)、0、false

var a = 1 && 2 + 2; // 4  第一个表达式为true,返回第二个表达式的值
var a = 0 && 2 + 2; // 0  第一个表达式为false,返回第一个表达式的值

作业:

n可输入 var n = parseInt(window.prompt('input'));

1、计算2的n次幂,n可输入,n为自然数

2、n的阶乘,n可输入

3、斐波那契数列 1 1 2 3 5 8输出第n项

4、输入一个三位数的正整数,反向输出。eg:输入456,输出654

5、输入a,b,c三个数字,打印出最大的

6、打印出1000以内的质数

1.4 条件语句

switch case; break; continue跳出本次循环,执行下次循环

switch(条件) {
    case 判断:
        ......;
        break; // 判断成立后跳出循环  下面的不执行,必须写在循环里面
    case 判断:
        ......
}

2. 对象

2.1 对象的创建方法

  • 1、字面量 var obj = {} 对象字面量/对象直接量

  • 2、构造函数

    • 系统自带的构造函数 new Object()

    • 自定义

  • 构造函数内部原理

    • 在函数体最前面隐式的加上this = {}

    • 执行 this.xxx = xxx;

    • 隐式的返回this

function Person(name, height) {
    // var this = {}
	this.name = name;
    this.height = height;
    this.say = function() {
        console.log(this.say);
    }
    // return {};   // 显式更改的,对象值、数组、function会被执行,person会返回Object{}。 
    // return 123;  // 显式更改的,改成原始值会被忽略,仍会执行return this;    new了就不可能返回原始值
    // return this; // 隐式执行的
}
var person = new Person('a', 1);
var person1 = new Person('b', 2);
  • 包装类new Number()、new String()、new Boolean()

    所有原始值都没有属性与方法,但在添加时并不会报错,因为包装类的存在

    var num = 4;
    num.len = 3; // 隐式执行new Number(4).len = 3; 但创建后就删除了delete
    // 到console.log时:执行new Number(4).len
    console.log(num.len);  // undefined
    
    var str = "abcd";
    str.length = 2; // new String('abcd').length = 2; delete 被截断的字符串'ab'已被销毁,跟之前定义的无关
    console.log(str); // abcd
    console.log(str.length); // 4  这个length是对象字符串本身就有的属性new String('abcd').length
    

试题:

var str = 'abc';
str += 1; // abc1
var test = typeof(str); // string
if(test.length == 6) { // test.length = 6
    test.sign = "typeof的返回结果可能是String";// 原始值赋属性值要调用包装类  new String(test).sign = 'xxx'
}
// new String(test).sign
console.log(test.sign);// undefined
function employee(name, code) {
    this.name = 'wangli';
    this.code  = "A001";
}
var newEmp = new employee("zhangming", "A002");// 虽然传值了,但是在函数里面并没有用到,this.name是写死的
document.write( newEmp.name ); // wangli
document.write( newEmp.code ); // A001
function Person(name, age, sex) {
    var a = 0;
    this.name = name;
    this.age = age;
    this.sex = sex;
    function sss() {
        a++;
        document.write(a);
    }
    this.say = sss;
}
var oPerson = new Person();
oPerson.say();  // 1
oPerson.say();  // 2
var oPerson1 = new Person();
oPerson1.say(); // 1
var x = 1, y = z = 0;
function add(n) {
    return n = n + 1;
}
y = add(x);
function add(n) {
    return n = n + 3;
}
z = add(x);
// 执行完毕 x = 1      y = 4       z = 4
// console.log结果是[1,2,3,4,5]的是
// A
function foo(x) {
    console.log(arguments)
    return x
}
foo(1,2,3,4,5)
// B 错
function foo(x) {
    console.log(arguments)
    return x
}(1,2,3,4,5) // 什么也不输出  是函数声明不是表达式
// C
(function foo(x) {
    console.log(arguments)
    return x
})(1,2,3,4,5)  // 实参列表 
// D
function foo() {
    bar.apply(null, arguments)
}
function bar(x) {
    console.log(arguments)
}
foo(1,2,3,4,5)
parseInt(3, 8) // 把3当成8进制    3
parseInt(3, 2) // NaN
parseInt(3, 0) // 3或NaN,不同浏览器的结果
function b(x, y ,a) {
    arguments[2] = 10;
    alert(a);
}
b(1,2,3); // 10
// 改变函数体为: 
a = 10; alert(arguments[2]); // 10
// 求一个字符串的字节长度  
// stringObject.charCodeAt(index)返回指定位置字符的unicode编码,返回值0-65535;<=255英文(一个字节),>255中文(两个字节)
function retByteslen(target) {
    var count = 0;
    for(var i = 0; i < target.length; i++) {
        if(target.charCodeAt(i) <= 255) {
            count++;
        } else if(target.charCodeAt(i) > 255) {
            count += 2;
        }
    }
    return count;
}
// 全都按英文计算,遇到中文时字节数加1
function retByteslen(target) {
    var count, len;
    count = len = target.length;
    for(var i = 0; i < len; i++) { // 每次循环都要计算target.length,可以先提取出来简便计算
        if(target.charCodeAt(i) > 255) {
            count++;
        }
    }
    return count;
}

3 函数

3.1 函数定义(函数声明、函数表达式)

// 函数声明
function theFirstName() {}  // 小驼峰命名   theFirstName.name = theFirstName    默认名字是自己
// 1. 命名函数表达式
var test = function abc() {
    document.write("a");
}
// console打印时,test是函数体,abc会显示没有定义  但是test.name = abc
// 2. 匿名函数表达式    ---    函数表达式
var demo = function () {
    document.write('b');
}
// demo.name = demo   默认名字是自己

3.2 组成形式

函数名称

参数:形参 实参

返回值return, 也是结束条件(没有返回值就直接return;表示结束 return 123; 返回值为123,同时结束)

所有的实参都存在实参列表arguments中,所有的形参都存在函数名中

// 实参较多
function sum(a) {
    console.log(arguments); // [1,2,3,4]
}
sum(1,2,3,4)
// 形参较多,arguments中仅有实参中的数,当形参a变化时,arguments[0]会随a的变化而变化,但是arguments[1]为undefined,不会随b的变化而改变
function sum(a, b, c, d) {
    console.log(sum); // ƒ sum(a, b, c, d) {console.log(sum);}
    console.log(sum.length); // 4
}
sum(1)

作业:

1. 写一个函数,告知你所选定的小动物的叫声
2. 写一个函数,实现加法计数器
3. 定义一组函数,输入数字,逆转并输出汉字形式
4. n的阶乘
5. 斐波那契数列
6.输入一串低于10位的数字,输出这串数字的中文大写
eg:input:10000  output:壹万
input:1001010   output:壹佰万壹仟零壹拾

作用域:变量(变量作用域又称上下文)和函数生效(能被访问)的区域

js

js执行步骤:语法分析、预编译、解释执行

函数声明整体提升 变量声明仅声明提升

预编译前奏

1、imply global暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有

eg: a=123; eg: var a=b=123;

2、一切声明的全局变量,全是window的属性

eg: var a=123; ===> window.a = 123;

预编译四部曲

1、创建AO对象(Activation Object)(执行期上下文)

2、找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

3、将实参值和形参统一

4、在函数体里面找函数声明,值赋予函数体

// 1
function fn(a) {
    console.log(a); // 1xx   function a() {}
    var a = 123;
    console.log(a); // 123
    function a() {}
    console.log(a); // 123
    var b = function () {}
    console.log(b);//function () {}
    function d() {}
}
fn(1);

AO {
    a: undefined ---> 1形参 ---> function a() {}函数体,    --->123赋值
    b: undefined, ---> function () {}
    d: function d() {}
}
// 2
function test(a, b) {
    console.log(a); // 1
    c = 0;
    var c;
    a = 3;
    b = 2;
    console.log(b); // 2
    function b() {}
    function d() {}
    console.log(b); // 2
}
test(1);

AO {
    a: undefined, ---> 1形参   ---> 3赋值
    b: undefined, ---> function b() {}函数体   ---> 2赋值
    c: undefined, ---> 0赋值
    d: function d() {}
    
}
// 3
function test(a, b) {
    console.log(a); // function a() {}
    console.log(b); // undefined
    var b = 234;
    console.log(b); // 234
    a = 123;
    console.log(a); // 123
    function a() {}
    var a;
    b = 234;
    var b = function () {}
    console.log(a); // 123
    console.log(b); // function () {}
}
test(1);

AO {
    a: undefined 形参  ---> 1 ---> function a() {}函数体 ---> 123赋值
    b: undefined 形参  ---> 234赋值 ---> 234赋值 ---> function () {}赋值
}
console.log(test);// function test(test) {...}
function test(test) {
    console.log(test); // function test() {}
    var test = 234;
    console.log(test); // 234
    function test() {}
}
test(1);
var test = 123;
// 全局GO 局部AO AO没有的去GO找
GO {
    test: undefined ---> function test(test) {...}---> 123
}
AO {
    test: undefined 形参 ---> 1 ---> function test() {}函数体 ---> 234赋值
}
//GO {
//    fn: function fn() {...}
//    global: undefined ---> 100
//}    
global = 100;
function fn() {
    console.log(global); // undefined
    global = 200;
    console.log(global); // 200
    var global = 300;
}
fn();
//AO {
//    global: undefined ---> 200 ---> 300
//}
var global;
// GO {
//		test: function test(){...}
// 		a: undefined ---> 10
//		c: 234
//}
function test() {
    console.log(b); // b is not definedxxx  undefined
    if(a) {
        var b = 100;
    }
    c = 234;
    console.log(c); // 234
}
var a;
test();
// AO {
//		b: undefined 与if执行与否无关
//}
a = 10;
console.log(c);// 234
function bar() {
    return foo;
    foo = 10;
    function foo() {}
    var foo = 11;
}
console.log(bar()); // function foo() {}
//AO {
//    foo: undefined ---> function foo() {}提升了 然后下面的不会执行
//}
//GO {
//   bar: function bar() {...}
//}
console.log(bar()); // 11
//AO {
//    foo: undefined ---> function foo() {} ---> 10 ---> 11
//}
function bar() {
    foo = 10;
    function foo() {}
    var foo = 11;
    return foo;
}
//GO {
//    demo: function demo(e) {...}
//    a: 100
//    f: 123
//}
a = 100;
function demo(e) {
    function e() {}
    arguments[0] = 2;
    document.write(e);// 2
    if(a) {
        var b = 123;
        function c() {}// 现在if里面不允许加function
    }
    var c;
    a = 10;
    var a;
    document.write(b); // undefined
    f = 123;
    document.write(c);//function c() {} 现实应该是undefined
    document.write(a);//10
}
var a;
demo(1);
//AO {
//    e: undefined 形参 ---> 1 ---> function e() {} ---> 2
//    b: undefined 
//    c: undefined 形参 ---> function c() {}
//    a: undefined ---> 10
//}
document.write(a);// 100
document.write(f);// 123
var str = false + 1;
document.wrtie(str);// 1
var demo = false == 1;
document.write(demo); // false
// "undefined" && "NaN" 都是字符串转为布尔值为true 可以进入循环     (-1 + NaN + 0 = NaN)
if(typeof(a) && -true + (+undefined) + "") {
    document.write('qqqqqq'); // qqqqqq
}
if(11 + "11"*2 == 33) {
    document.write('qqqqqq'); // qqqqqq
}
// true + false - false = 1 || document.write('dddddd')后面的不执行不打印
!!" " + !!"" - !!false||document.write('dddddd')
(window.foo || (window.foo = 'bar'));
// ()优先级大于 || 所以(window.foo = 'bar')先执行 最后window.foo = 'bar'而不是"undefined"

ctrl +’’ 去标签

ctrl + f 查找 find all 全部选择要查找的项 然后可以全部改变

4 作用域

[[scope]]: 每个javascript函数都是一个对象,对象中有些属性可以访问,有些不可以,不可以访问的这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]就是作用域,其中存储了运行期上下文的集合

作用域链: [[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链接叫做作用域链。

运行期上下文: 当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次

执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行期上下

文被销毁。

查找变量: 从作用域链的顶端依次向下查找。

function a() {
    function b() {
        var b = 234;
        a = 10;
    }
    var a = 123;
    b();
    console.log(a); // 10 b函数执行完后,访问a,拿的就是a的AO(引用),在b执行后,a的值已经被改变成了10
}
var glob = 100;
a();
// a定义  a.[[scope]] ---> 0: GO {function a(){...},glod:100}
// a执行  a.[[scope]] ---> 0: AO {function b(){...},a:123}
//					   	  1: GO {function a(){...},glod:100}
// 因为b定义的时候在a里面,所以b最先的作用域就是a的AO
// b定义  b.[[scope]] ---> 0: AO {function b(){...},a:123}   a的AO
//					   	  1: GO {function a(){...},glod:100}
// b执行  b.[[scope]] ---> 0: AO {b:234}   b的AO
//                        1: AO {function b(){...},a:123}
//                        2: GO {function a(){...},glod:100}
function a() {
    function b() {
        var bbb = 234;
        document.write(aaa); //123
    }
    var aaa = 123;
    return b; // 闭包
}
var glob = 100;
var demo = a();// demo就相当于b,把b给重新保存起来了,当a断了之后也不受影响
demo();
// a定义  a.[[scope]] ---> 0: GO {function a(){...},glod:100,demo:a()}
// a执行  a.[[scope]] ---> 0: AO {function b(){...},aaa:123}
//					   	  1: GO {function a(){...},glod:100,demo:a()}
// demo定义 demo.[[scope]] ---> 0: AO {demo: function b(){...}}
//							   1: GO {function a(){...},glod:100,demo:a()}
// demo执行 demo.[[scope]] ---> 0: {bbb: 234}
// 							   1: {demo: function b(){...}}
//    						   2: GO {function a(){...},glod:100,demo:a()}
function a() {
    var num = 100;
    function b() {
        num++;
        console.log(num);
    }
    return b;
}
var demo = a();
demo();
demo();
// a定义  a.[[scope]] ---> 			0: GO {function a(){...},demo:a()}
// a执行  a.[[scope]] ---> 			0: aAO {num: 100,function b(){...}}
//					   	 		 	 1: GO {function a(){...},demo:a()}
// demo定义 demo.[[scope]] ---> 		0: aAO {num: 100,function b(){...}}
//							   		 1: GO {function a(){...},demo:a()}
// demo执行 demo.[[scope]] ---> 		0: demoAO {}
// 							   		 1: aAO {num: 100-->101,function b(){...}}
//    						   		 2: GO {function a(){...},demo:a()}
// demo二次执行 demo.[[scope]] --->   0: demoAO {}
// 							   		 1: aAO {num: 101-->102,function b(){...}}
//    						   		 2: GO {function a(){...},demo:a()}

5 闭包

当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄漏。

闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止出现这种情况

闭包的作用:

1、实现公有变量 eg:函数累加器

// 累加器
function add() {
    var count = 0;
    function demo() {
        count++;
        console.log(count);
    }
    return demo;
}
var counter = add();
counter(); // 1
counter(); // 2
counter(); // 3

2、可以做缓存(存储结构) eg:eater

function test() {
    var num = 100;
    function a() {
        num++;
        console.log(num); // 101
    }
    function b() {
        num--;
        console.log(num); // 100
    }
    return [a,b];
}
var myArr = test();
myArr[0]();
myArr[1]();
// test定义      test.[[scope]]   --->  0: GO {a: function(){...}, myArr: test()}
// test执行      test.[[scope]]   --->  0: testAO {num: 100, a: function, b: function}
// 							   	       1: GO {a: function(){...}, myArr: test()}
// myArr[0]定义 myArr[0].[[scope]] ---> 0: testAO {num: 100, a: function, b: function}
// 							   	       1: GO {a: function(){...}, myArr: test()} 
// myArr[0]执行 myArr[0].[[scope]] ---> 0: myArr[0]AO {}
// 									   1: testAO {num: 100--> 101, a: function, b: function}
// 							   	       2: GO {a: function(){...}, myArr: test()}
// myArr[1]定义 myArr[1].[[scope]] ---> 0: testAO {num: 101, a: function, b: function}
// 							   	       1: GO {a: function(){...}, myArr: test()} 
// myArr[1]执行 myArr[1].[[scope]] ---> 0: myArr[1]AO {}
// 									   1: testAO {num: 101--> 100, a: function, b: function}
// 							   	       2: GO {a: function(){...}, myArr: 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(); // i am eating banana

3、可以实现封装。属性私有化 eg:Person()

function Deng(name, wife) {
    var prepareWife = "xiaozhang";
    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 deng = new Deng('deng', 'xiaoliu');
deng // Deng {name: "deng", wife: "xiaoliu", divorce: ƒ, changePrepareWife: ƒ, sayPrepareWife: ƒ}
deng.divorce();
deng.wife // "xiaozhang"  this.wife能够变化是因为闭包,divorce执行后能够用到Deng的AO
// 但是deng.preparWife并没有,相当于preparWife是其私有化的属性

4、模块化开发,防止污染全局变量(命名空间)

var name = "123";
var init = (function () {
    var name = 'abc';
    function callName() {
        console.log(name);
    }
    return function () {
        callName();
    }
}());
init(); // abc

var init2 = (function () {
    var name = 'wer';
    function callName() {
        console.log(name);
    }
    return function () {
        callName();
    }
}());
init2(); // wer
// 不会影响全局中相同名字的变量,各个局部的变量之间也不受影响

测试:

function test() {
    var arr = [];
    for(var i = 0; i < 10; i++) { // 改为let i = 0就可以打印出0 1 2 3 4 5 6 7 8 9
        arr[i] = function () {
            console.log(i); // 打印出来十个 10,因为在循环结束后i=10,所有的myArr[j]连的都是testAO
        }
    }
    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++) { 
        // 用立即执行函数打印出来0 1 2 3 4 5 6 7 8 9
        (function (j) {
            arr[j] = function () {
                console.log(j);
            }
        }(i));
    }
    return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
    myArr[j]();
}

题:

// <ul>
//     <li>a</li>
//     <li>a</li>
//     <li>a</li>
//     <li>a</li>
// </ul>
// 使用原生js,addEventListener,给每个li元素绑定一个click事件,输出他们的顺序
function test () {
	var liCollection = document.getElementsByTagName('li');
    for(var i = 0; i < liCollection.length; i++) {
        (function (j) { // 不用立即执行函数会输出4个4,不会输出他们的索引值
            liCollection[j].onclick = function () { // liCollection[j].addEventListener('click', function())
                console.log(j);
            }
        }(i))
    }
}
test();
// , 把最后一个值赋给变量  var a = (1-1, 1+1); // 2
var f = (
	function f() {
        return "1";
    },
    function g() {
        return 2;
    }
)();
typeof f; // "number"
var x = 1;
if(function f() {}) { // ()把函数f转换成表达式,最后返回的不是false,之后函数f也销毁
    x += typeof f; // "1undefined"
}
x;

6 立即执行函数

定义:此类函数没有声明,在一次执行过后即释放。适合做初始化工作

形式:( function (形参) {…} (实参) ); 或者 ( function (形参) {…} ) (实参); 括号里面是匿名函数,不用起名,可以有返回值

var num = (function (a, b, c) {
    var d = a + b + c * 2 - 2;
    return d;
}(1, 2, 3));
console.log(num); // 7

只有表达式才能被执行符号执行,能被执行符号()执行的表达式,这个函数的名字会被自动忽略,也就相当于是立即执行函数,执行完就没有了

function test() {
    var a = 123;
}();  	// 这样函数不能被执行,因为是函数声明不是表达式,只有test();才能执行

var test = function () {
    console.log('a');
}()  // 函数表达式可以被执行

+ function test() {
    console.log('a');
}() // 前面的符号是+ - !可以被执行 之后再打印test就显示 test is not defined;  * / 不可以

function test(a, b, c, d) {
    console.log(a + b + c + d);
}(1, 2, 3, 4)  // 不报错但是也不执行
console.log(test); // function test() {...}
// 相当于
function test(a, b, c, d) {
    console.log(a + b + c + d);
}
(1, 2, 3, 4);

7 原型

1、定义

原型是function对象的一个属性,定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。

// Person.prototype -- 原型
Person.prototype.LastName = "Zhang"; // 原型属性后代会继承
Person.prototype.say = function () {
    console.log('hehe');
}
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
var person = new Person('haha', 18, 'male'); // 以自己有的属性为主,自己没有就去原型找
person.LastName  // Zhang

2、利用原型特点和概念,可以提取共有属性

Car.prototype.height = 1400; // 共有属性
Car.prototype.lang = 4900;   // 共有属性
Car.prototype.carName = "BMW"; // 共有属性
// 简便
Car.prototype = {
    height: 1400,
    lang: 4900,
    carName: "BMW"
}
function Car(color, owner) {
    this.color = color;
    this.owner = owner;
}
var car = new Car('red', 'zhang'); 
var car = new Car('green', 'li'); 

原型的"增删改查"必须作用到prototype上:delete car.height后显示true,但是``car.height`仍然是1400,并没有影响原型,只影响自己

delete Car.prototype.height 可以删除 Car.prototype.height = 1300;可以修改

3、对象如何查看原型 —> 隐式属性 __proto__

Person.prototype.name = 'abc';
function Person() {
    // var this = {
    //		__proto__: Person.prototype
	// }
}
var obj = {
    name: "sunny"
}
var person = new Person();
person.__proto__ = obj;
person.name  // "sunny"
Person.prototype.name = 'sunny';
function Person() {}
Person.prototype.name = 'cherry'; // cherry
var person = new Person();
Person.prototype.name = 'cherry'; // cherry
Person.prototype = { // sunny
    name: 'cherry'
}
person.name // Person.prototype.name不论更改的位置在哪,结果都会改变(在原有的基础上改变属性值)
// Person.prototype = {...}只有放在var person = new Person();前面,更改的内容才会变化 (把原型给改变了)
// var obj = {name: "a"};
// var obj1 = obj;
// obj = {name: "b"};
// obj1.name // "a"
// obj.name  // "b"
Person.prototype.name = 'sunny';
function Person() {
    // var this = {
    //		__proto__: Person.prototype
	// }
}
var person = new Person();
Person.prototype = {  // Person.prototype换了个新空间,但是__proto__指向的还是原来的空间 
    name: 'cherry'
}
person.name  // sunny
// Person.prototype = {name: "a"};
// __proto__ = Person.prototype;
// Person.prototype = {name: "b"};

4、对象如何查看对象的构造函数 —> constructor(可以手动更改)

function Car() {}
var car = new Car();
car.constructor  // ƒ Car() {}
Car.prototype 	// constructor: ƒ Car()
				// __proto__: Object
// 更改 constructor
function Person() {}
Car.prototype = {
    constructor: Person
}
function Car() {}
var car = new Car();
// 如果更改发生在var car = new Car();之后,car.constructor还是 ƒ Car() {}
car.constructor  // ƒ Person() {}  
Car.prototype 	// constructor: ƒ Person()
				// __proto__: Object

8 原型链

1、如何构成原型链

// Grand.prototype.__proto__ = Object.prototype  所有对象的最终原型
Grand.prototype.lastName = "Deng";
function Grand() {}
var grand = new Grand();
Father.prototype = grand;
function Father() {
    this.name = "xuming";
    this.fortune = {
        card1: "visa"
    };
    this.num = 100;
}
var father = new Father();
Son.prototype = father;
function Son() {
    this.hobbit = "smoke"
}
var son = new Son();
son.fortune.card2 = "master"; // 不是覆盖更改,是调用更改,引用值自己修改。但是原始值只能覆盖
father.fortune // {card1: "visa", card2: "master"}
son.num++;
father.num // 100
son.num // 101 只更改自己的num值
// a.sayName() sayName里面的this指向: 谁调用的这个方法,this就指向谁
Person.prototype = {
    name: "a",
    sayName: function () {
        console.log(this.name);
    }
}
function Person () {
    this.name = "b";
}
var person = new Person();
person.sayName() // b
Person.prototype.sayName() // a

2、原型链上属性的增删改查

Person.prototype = {
    height: 100
}
function Person () {
    this.eat = function () {
        this.height++;
    }
}
var person = new Person();
person.eat()
person.height // 101
person.__proto__.height // 100

3、绝大多数对象最终都会继承自Object.prototype

Person.prototype = {} ---> Object.prototype原型链终端
function Person() {}

例外:

var obj = Object.create(null); // 没有属性
obj // {}
		// No properties
// 可以加属性
obj.name = "123";
obj // name: "123"
// 加个原型
obj.__proto__ = {name: "sunny"};
obj   // {name: "123", __proto__: {…}}
		// name: "123"
		// __proto__:
			// name: "sunny"
			// __proto__: Object
// 但是并不被系统承认,obj.name访问不到
var obj = Object.create(null);
obj.__proto__ = {name: "sunny"}; // __proto__原型是隐式的内部属性
obj.name // undefined

undefined null没有包装类,也没有原型,就是个原始值,没有toString方法

4、Object.create(原型prototype, 特性definedProperty);

var obj = {name: "sunny", age:13};
var obj1 = Object.create(obj); // obj1的原型就是obj
obj1 // {}
	// __proto__: 
		// age: 13
		// name: "sunny"
		// __proto__: Object
Person.prototype.name = "sunny";
function Person() {}
var person = Object.create(Person.prototype); 
person // Person {}
		// __proto__:
			// name: "sunny"
			// constructor: ƒ Person()
				// arguments: null
				// caller: null
				// length: 0
				// name: "Person"
				// prototype: {name: "sunny", constructor: ƒ}
				// __proto__: ƒ ()
				// [[FunctionLocation]]: VM903:2
				// [[Scopes]]: Scopes[1]
			// __proto__: Object

一旦经历了var的操作,所得出的属性即window上的属性,这种属性叫做不可配置属性

不可配置的属性 delete不掉

var num = 123;

delete window.num // false

9 继承

发展过程

1、传统形式 —> 原型链 (grand, father, son)

过多的继承了没用的属性

2、借用构造函数(Person, Student call apply)

不能继承借用构造函数的原型

每次构造函数都要多走一个函数

3、共享原型

不能随便改变自己的原型,会连着把father的原型也给改了

Father.prototype.lastName = "Deng";
function Father() {}
function Son() {}
Son.prototype = Father.prototype;
var son = new Son();
son.lastName  // "Deng"

封装成函数

Father.prototype.lastName = "Deng";
function Father() {}
function Son() {}
function inherit(Target, Origin) {
    Target.prototype = Origin.prototype;
}
inherit(Son, Father); // 要写在var son = new Son(); 先继承再使用
var son = new Son();
son.lastName  // "Deng"

4、圣杯模式 = 共享原型 + 原型链

Father.prototype.lastName = "Deng";
function Father() {}
function Son() {}
function inherit(Target, Origin) {
    function F() {}
    F.prototype = Origin.prototype; // 必须在Target.prototype = new F();前面,不然无用
    Target.prototype = new F();
    Target.prototype.constuctor = Target; // 让Target的构造函数仍然是自己,要不会继承自father的
    Target.prototype.uber = Origin.prototype; // 保存Target最终的原型方便后期使用
}
inherit(Son, Father); // 要写在var son = new Son(); 先继承再使用
var son = new Son();
son.lastName  // "Deng"

闭包的第三个作用: 3、可以实现封装。属性私有化 eg:Person()

function Deng(name, wife) {
    var prepareWife = "xiaozhang";
    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 deng = new Deng('deng', 'xiaoliu');
deng // Deng {name: "deng", wife: "xiaoliu", divorce: ƒ, changePrepareWife: ƒ, sayPrepareWife: ƒ}
deng.divorce();
deng.wife // "xiaozhang"  this.wife能够变化是因为闭包,divorce执行后能够用到Deng的AO
// 但是deng.preparWife并没有,相当于preparWife是其私有化的属性

利用此特性进一步封装inherit

var inherit = (function () {
    var F = function () {}; // 私有化变量
    return function (Target, Origin) {
    	F.prototype = Origin.prototype;
        Target.prototype = new F();
        Target.prototype.constuctor = Target;
        Target.prototype.uber = Origin.prototype;
	}
})

10 命名空间

管理变量,防止污染全局,适用于模块化开发

var name = "123";
var init = (function () {
    var name = 'abc';
    function callName() {
        console.log(name);
    }
    return function () {
        callName();
    }
}());
init(); // abc

var init2 = (function () {
    var name = 'wer';
    function callName() {
        console.log(name);
    }
    return function () {
        callName();
    }
}());
init2(); // wer
// 不会影响全局中相同名字的变量,各个局部的变量之间也不受影响

模仿jquery的连续调用方法

var deng = {
    smoke: function () {
        console.log('smoking...');
        return this;
    },
    drink: function () {
        console.log('drinking...');
        return this;
    },
    perm: function () {
        console.log('perming...');
        return this;
    }
}
deng.smoke().drink().perm(); // smoking...   drinking...    perming...

属性表示方法

obj.prop隐式转化为obj["prop"]

obj["prop"]属性拼接

var deng = {
    name1: {name: "zhangsan"},    
    name2: {name: "lisi"},
    name3: {name: "wanger"},
    name4: {name: "mawu"},
    sayName: function (num) {
        return this['name' + num];
    }
}
deng.sayName(1); // {name: "zhangsan"}
deng.sayName(2); // {name: "lisi"}

对象枚举(es6-es11)

var x = 1, y = z = 0;
function add(n) {
    return n = n + 1;
}
y = add(x); // y = 4  x = 1
function add(n) {
    return n = n + 3;
}
z = add(x); // z = 4

原始值相比较时比较的是值,引用值比较的是地址

123 == 123 // true 但是 {} == {} // false

11 this

1、函数预编译过程 this —> window

function test(c) {
    // var this = Object.create(test.prototype); // new test();之后才改变this的,否则一直是window
    var a = 123;
    function b() {}
}
// AO {
//     arguments: [1],
//     this: window,
//     c: 1,
//     a: undefined,
//     b: function () {}
// }
test(1);
new test();

2、全局作用域里 this —> window

3、call apply 可以改变函数运行时this指向

4、obj.func(); func()里面的this 指向 obj即谁调用这个函数this就指向谁,没人调用指向window

var obj = {
    a: function () {
        console.log(this.name); // obj调用的该方法,因此输出abc
    },
    name: 'abc'
}
obj.a();

题:

var name = "222";
var a = {
    name: "111",
    say: function () {
        console.log(this.name);
    }
}
var fun = a.say; // function () {console.log(this.name)}
fun() 	// 222 全局执行函数 没有调用
a.say() // 111	a调用
var b = {
    name: "333",
    say: function (fun) {
        // this ---> b
        fun();//但是fun 谁也没调用 走预编译
    }
}
b.say(a.say);  // 222
b.say = a.say; // function () {console.log(this.name)}
b.say(); // 333

12 arguments

arguments.callee 即这个函数的引用 立即执行函数可能用到

function test() {
    console.log(arguments.callee);  // ƒ test() {console.log(arguments.callee);} 就是test函数
    console.log(test.caller);  // null
}
test();
// 求100的阶乘 需要用到前面函数执行的结果 但是立即执行函数执行过后并不能找到
var num = (function (n) {
    if(n == 1) {
        return 1;
    } 
    return n * arguments.callee(n - 1);
}(100))

func.caller 在哪个环境里面被调用的·

function test () {
    demo();
}
function demo() {
    console.log(demo.caller); // ƒ test () {demo();}
}
test();

在ES5环境中 “use strict”; 会报错 不能使用callee和caller

题:

// 1
var foo = '123';
function print() {
    var foo = '456';
    this.foo = "789"; // this指向window
    console.log(foo); // 456
}
console.log(foo); // 789
print(); 
// 2
var foo = '123';
function print() {
    this.foo = "789";
    console.log(foo); // 789 自己没有上window找foo
}
print();
// 3
var foo = '123';
function print() {
    // var this = Object.create(print.prototype)
    this.foo = "789"; // 这个时候的this不指向window
    console.log(foo); // 123
}
new print();
// 1. 运行test() 和 new test() 的结果分别是什么
var a = 5;
function test() {
    a = 0;
    alert(a);           // test(): 0            new test():0
    alert(this.a);		// test(): 5            new test():0 X  undefined
    var a;
    alert(a);			// test(): 0            new test():0
}
function print() {
    console.log(foo); // 2x   undefined
    var foo = 2;
    console.log(foo); // 2
    console.log(hello); // hello is not defined
}
print();
function print() {
    var test;
    test();
    function test() {
        console.log(1);
    }
}
// AO {
//     test: undefined ---> test()
// }
print(); // 1
function print() {
    var x = 1;
    if(x == "1") console.log("One!");
    if(x === "1") console.log("Two!");
}
print(); // One!
function print() {
    var marty = {
        name: "marty",
        printName: function() {console.log(this.name);}
    }
    var test1 = { name: "test1" };
    var test2 = { name: "test2" };
    var test3 = { name: "test3" };
    test3.printName = marty.printName;
    var printName2 = marty.printName.bind({ name: 123 });
    marty.printName.call(test1);  // test1
    marty.printName.apply(test2); // test2
    marty.printName(); // marty
    printName2(); // 123
    test3.printName(); // test3
}
print();
var bar = { a: "002" }; // {a: 'a'}在打印的时候值已经更改了
function print() {
    bar.a = 'a';
    Object.prototype.b = 'b';
    return function inner() {
        console.log(bar.a); // 002 X  a
        console.log(bar.b); // b
    }
}
print()();

作业: 浅层 深层克隆

 // 浅层克隆 原始值obj与obj1的改变互不影响   引用值obj与obj1的改变相互影响
var obj = {
    name: 'abc',
    age: 123,
    sex: 'female',
    card: ["visa", "unionpay"]
}
var obj1 = {}
function clone(origin, target) {
    var target = target || {}; // 如果没有传target就新建一个空对象
    for(var prop in origin) {
        target[prop] = origin[prop];
    }
    return target;
}
clone(obj, obj1);
obj1 // {name: "abc", age: 123, sex: "female", card: Array(2)}
// 原始值obj与obj1的改变互不影响
obj.last="haha";
obj1 // {name: "abc", age: 123, sex: "female", card: Array(2)}
obj  // {name: "abc", age: 123, sex: "female", card: Array(2), last: "haha"}
// 引用值obj与obj1的改变相互影响
obj.card.push("master")
obj1 // {name: "abc", age: 123, sex: "female", card: Array(3)}
obj  // {name: "abc", age: 123, sex: "female", card: Array(3), last: "haha"}
// 深层克隆 互不影响
var obj = {
    name: 'abc',
    age: 123,
    card: ["visa", "unionpay"],
    wife: {
        name: "bcd",
        son: {
            name: "aaa"
        }
    }
}
var obj1 = {};
// 遍历对象 for(var prop in obj)
// 1. 判断对象是不是原始值  typeof()  object
// 2. 判断是数组还是对象 instanceof toString constructor
// 3. 建立相应的数组或对象
// 4. 递归
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" && typeof(origin[prop]) == 'object') {
                // if(toStr.call(origin[prop]) == arrStr) {
                //     target[prop] = [];
                //  } else {
                //     target[prop] = {};
                // }
                // 用三目运算符简化
                target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
                deepClone(origin[prop], target[prop]); // 递归
            } else {
                target[prop] = origin[prop];
            }
    	}
    }
    return target;
}
deepClone(obj, obj1);
obj   // {name: "abc", age: 123, card: Array(2), wife: {…}}
		// age: 123
		// card: (2) ["visa", "unionpay"]
		// name: "abc"
		// wife:
			// name: "bcd"
			// son: {name: "aaa"}
obj1	// {name: "abc", age: 123, card: Array(2), wife: {…}}
		// age: 123
		// card: (2) ["visa", "unionpay"]
		// name: "abc"
		// wife:
			// name: "bcd"
			// son: {name: "aaa"}
// obj变化 obj1不变
obj.card.push("master");
obj	 // {name: "abc", age: 123, card: Array(3), wife: {…}}
obj1 // {name: "abc", age: 123, card: Array(2), wife: {…}}

13 数组

数组

定义:
  • new Array(length/content); 只传一个参数时默认是数组的长度length,且不能是小数

  • 字面量 var arr = [];

数组的读和写:
  • arr[num] // 不可以溢出读 结果是undefined

  • arr[num] = xxx; // 可以溢出写

数组常用方法:
  • 改变原数组: push pop shift unshift sort reverse splice

  • 不改变原数组: concat join split toString slice

仿写push方法:

// 每次都往原数组的最后一位加push的内容,返回push后的数组长度
var arr = [1, 2, 3];
Array.prototype.push = function () {
    for(var i = 0; i < arguments.length; i++) {
        this[this.length] = arguments[i];
    }
    return this.length;
}

pop: 剪切数组的最后一位,返回该值

shift:从数组的最前面减

unshift:往数组的最前面加

reverse: 把原数组逆转顺序

splice:arr.splice(从第几位开始,截取长度,在切口处添加新的数据) 返回截取的值

-1位表示最后一位,splice在定义第几位时,

splice = function (pos) {
    pos += pos > 0 ? 0 : this.length;
}

var arr = [2, 3, 5, 7];
arr.splice(-1, 0, 9);  // -1 + 4 = 3,从第三位开始加
arr // [2, 3, 5, 9, 7]

sort:对数组排序,按ASCII码排

var arr = [1, 3, 6, 10, 34, 89, 9];
arr.sort();  // [1, 10, 3, 34, 6, 89, 9]
// sort可以接收函数,按照自己的方式排序
// 要求: 1. 必须写俩形参    2. 排序结果看返回值   
// 							2.1 当返回值为正数时,后面的数在前    升序
// 							2.2 当返回值为负数时,前面的数在前	   升序
// 							2.3 当返回值为0,不动
arr.sort(function(a, b) {
    return a - b; // [1, 3, 6, 9, 10, 34, 89]		升序
    return b - a; // [89, 34, 10, 9, 6, 3, 1]		降序
});
// 给一个随机数组乱序
var arr = [1, 2, 4, 8, 9];
// Math.random(); // 产生(0, 1)之间的随机数
arr.sort(function () {
    return Math.random() - 0.5; // 正负值不定
});
var cheng = {
    name: "cheng",
    age: 18,
    sex: "male",
    face: "handsome"
}
var deng = {
    name: "deng",
    age: 38,
    sex: "male",
    face: "haha"
}
var zhang = {
    name: "zhang",
    age: 27
}
// 按年龄排序
var arr = [cheng, deng, zhang];
arr.sort(function (a, b) {
    return a.age - b.age;   // 0: {name: "cheng", age: 18, sex: "male", face: "handsome"}
							// 1: {name: "zhang", age: 27}
							// 2: {name: "deng", age: 38, sex: "male", face: "haha"}
});
// 按字节长度排序
function retBytes(str) {
    var num = str.length;
    for(var i = 0; i < str.length; i++) {
        if(str.charCodeAt(i) > 255) {
            num++;
        }
    }
    return num;
}
var arr = ["habg", "哈", "hagyq哈2276287哈yf", "as哈cu", "hjahgsty6wqppqjhhg ffjhdkldlkd"];
arr.sort(function(a, b) {
    return retBytes(a) - retBytes(b); 
    // ["哈", "habg", "as哈cu", "hagyq哈2276287哈yf", "hjahgsty6wqppqjhhg ffjhdkldlkd"]
});

不改变原数组:

var arr1 = [2, 3, 5, 7];
var arr2 = [2, 6, 9, 7];
arr1.concat(arr2); // 把arr2拼到arr1的后面   // [2, 3, 5, 7, 2, 6, 9, 7]
arr1	// [2, 3, 5, 7]
arr2	// [2, 6, 9, 7]                                   
// arr.slice(从第几位开始截取, 截取到哪位不包括该位) 返回截取的数据 不会改变原数组 如果需要该数据需要另赋值
var arr = [1, 2, 5, 8];
var newArr = arr.slice(1, 3); // [2, 5]
var newArr = arr.slice(1); // [2, 5, 8]  只填一个参数,从该位开始截到最后
var newArr = arr.slice(); // [1, 2, 5, 8] 不填参数 全部截取
// arr.join() 传字符串形式的  把数组每一位用所传的字符串连接 返回字符串
var str = arr.join()		// "1,2,5,8"
var str = arr.join("")	// "1258"
var str = arr.join("-")	// "1-2-5-8"
// str.split()  按所传字符串拆分,拆分字符串为数组
str.split("-") // ["1", "2", "5", "8"]
str.split("")  // ["1", "-", "2", "-", "5", "-", "8"]
str.split(" ") // ["1-2-5-8"]
str.split()    // ["1-2-5-8"]
// 字符串连接 栈内存连接 先进后出      数组  堆  散列结构
// 字符串拼接
var str = "alibaba";
var str1 = "baidu";
var str2 = "tencent";
var str3 = "toutiao";
var str4 = "wangyi";
var str5 = "haha";
var str6 = "kajajssis";
var arr = [str, str1, str2, str3, str4, str5, str6];
arr.join("");  // "alibababaidutencenttoutiaowangyihahakajajssis"
arr.join("-"); // "alibaba-baidu-tencent-toutiao-wangyi-haha-kajajssis"
arr.join()     // "alibaba,baidu,tencent,toutiao,wangyi,haha,kajajssis"

类数组arguments

1、可以利用 属性名 模拟数组的特性

2、可以动态增长length属性

3、如果强行让类数组调用push方法,会根据length属性值的位置进行属性的扩充

// 属性要为索引(数字)属性,必须有length属性,最好加上push
var obj = {
    "0": "a",
    "1": "b",
    "2": "c",
    "length": 3,
    "push": Array.prototype.push,
    "splice": Array.prototype.splice  // 加上obj的样式变成数组
}
obj.push('d');
obj  // {0: "a", 1: "b", 2: "c", 3: "d", length: 4, push: ƒ}
// 要完全变成数组的样子,要加splice方法
obj // ["a", "b", "c", "d", push: ƒ, splice: ƒ]

题:

var obj = {
    "2": "a",
    "3": "b",
    "length": 2,
    "push": Array.prototype.push
}
obj.push('c');
obj.push('d');
obj  // {2: "c", 3: "d", length: 4, push: ƒ}
Array.prototype.push = function (target) {
    obj[obj.length] = target;   // obj[2] = c       obj[3] = d
    obj.length++;				// length: 3	    length: 4
}
// 封装type  可以返回具体的类型
// [] -- array , {} -- object , function -- function , new Number() -- [object Number] , 123 -- number
// 不完善
function setType(target) {
    var toStr = Object.prototype.toString,
        arrStr = "[object Array]",
        numStr = "[object Number]";
    if(toStr.call(target) == arrStr) {
    	return 'array';
    } else if(toStr.call(target) == numStr) {
        return numStr;
    } else if(target === null) {
        return "null";
    } else {
        return typeof(target);
    }
}

// 
function type(target) {
    var ret = typeof(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(ret == "object") {
        var str = Object.prototype.toString.call(target);
        return template[str];
    } else {
        return ret;
    }
}
// 数组去重		要求在原型链上编程
Array.prototype.unique = function () {
    var newArr = [],
        temp = {},
        len = this.length;
    for(var i = 0; i < len; i++) {
        if(!temp[this[i]]) {
            temp[this[i]] = "a"; // 随便赋的值,主要看属性名
            newArr.push(this[i]);
        }
    }
    return newArr;
}
var arr = [1,"h",2,2,"ha","a",3,4,3,2,455,12,2,12,"ha"];
arr.unique(); // [1, "h", 2, "ha", "a", 3, 4, 455, 12]

题:

function Person(name, age, sex) {
    var a = 0;
    this.name = name;
    this.age = age;
    this.sex = sex;
    function sss() {
        a++;
        document.write(a);
    }
    this.say = sss;
} 
var oPerson = new Person();
oPerson.say(); // 1
oPerson.say(); // 2
var oPerson1 = new Person();
oPerson1.say(); // 3xx    1
(function (x) {
    delete x;  // 形参相当于var  不能delete
    return x;
})(1); // 1
function test() {
	console.log(typeof(arguments)); // object
}
test();
var h = function a() {
    return 23;
}
// a写在表达式里面 最后就没有了
console.log(typeof a()); // Uncaught ReferenceError: a is not defined

作业:

1、一个字符串[a-z]组成,请找出该字符串第一个只出现一次的字母

function firstAlphabet(str) {
    for(var i = 0; i < str.length; i++) {
        
    }
}

2、字符串去重

14 try…catch

try {
    // 在try里面发生的错误,不会执行错误后的try里面的代码,接着执行catch里面的命令与try外面的命令    不会让后续代码终止
}catch(e) {
    // 如果try里面发生错误,会执行catch里面的命令
    // 自动保存error,有两个值error.message  error.name。可自己处理错误信息
} finally {}

Error.name的六种值对应的信息:

1、EvalError: eval()的使用与定义不一致

2、RangeError:数值越界

3、ReferenceError:非法或不能识别的引用数值

4、SyntaxError:发生语法解析错误

5、TypeError:操作数类型错误

6、URIError:URI处理函数使用不当

15 es5严格模式

“use strict”;

  • 不再兼容es3的一些不规则语法。使用全新的es5规范
  • 两种用法
    • 全局严格模式
    • 局部函数内严格模式(推荐)
  • 即一行字符串,不会对不兼容严格模式的浏览器产生影响
  • 不支持with, arguments.callee, func.caller,变量赋值前必须声明,局部this必须被赋值(Person.call(null/undefined)赋值什么就是什么),拒绝重复属性和参数
var obj = {
    name: "obj"
}
var name = "window";
function test() {
    var name = "scope";
    with(obj) { // with可以改变作用域链,with作用域链的最顶端是obj对象,obj对象充当里面代码的AO
        console.log(name); // obj
    }
}
test();
// 非严格模式的this指向window
"use strict";
function test() {
    console.log(this);   
}
test();  // undefined  不再指向window
new test(); // new的话会显示一个新对象  打印出: test {}     名字加对象的标准形式
test.call({});  // Object {}
test.call(123);  // 严格模式显示123  非严格模式会显示成包装类 
// 非严格模式相当于  new Number(123)
	//Number {123}
		//__proto__: Number
		//[[PrimitiveValue]]: 123
// eval把字符串当做代码执行  es3.0不能使用  
"use strict";
var a = 123;
eval('console.log(a)'); // 123
// eval可以改变作用域

16 dom

1、DOM — Document Object Model 文档对象模型

2、DOM定义了表示和修改文档所需的方法。DOM对象即为宿主对象,由浏览器厂商定义,用来操作html和xml功能的一类对象的集合。或者称DOM是html和xml的标准编程接口

3、基本操作

查看元素节点
  • document 整个文档

  • document.getElementById() 元素id在ie8以下的浏览器中,不区分id大小写,也返回匹配name属性的元素

  • .getElementsByTagName() 标签名

  • .getElementsByName() 只有部分标签name可生效(表单,表单元素,img,iframe)

  • .getElementsByClassName() 类名 ie8及以下的ie版本中没有,可以多个class一起

  • .querySelector() css选择器 ie7及以下版本没有 不实时(相当于取的是原来的副本,对其操作不会影响原来的)

  • .querySelectorAll() css选择器 ie7及以下版本没有 不实时

<div></div>
<script type="text/javascript">
	var div = document.getElementsByTagName('div')[0];
    div.style.width = "100px";
    div.style.height = "100px";
    div.style.backgroundColor = "red";
    // 点击事件  点击div后变绿,再点击变红
    var count = 0;
    div.onclick = function() {
        count++;
        if(count % 2 == 1) {
            div.style.backgroundColor = "green";
        }else {
            div.style.backgroundColor = "red";
        }
    }
</script>
// 选项卡功能  点击显示不同的块
<div class="wrapper">
    <button class="active">111</button>
    <button>222</button>
    <button>333</button>
    <div class="content" style="display: block;">111111111</div>
    <div class="content">222222222</div>
    <div class="content">333333333</div>
</div>
<script type="text/javascript">
    var btn = document.getElementsByTagName('button');
    var div = document.getElementsByClassName('content');
    for(var i = 0; i < btn.length; i++) {
        (function(n) {
            btn[n].onclick = function() {
                for(var j = 0; j < btn.length; j++) {
                    btn[j].className = "";
                    div[j].style.display = "none";
                }
                this.className = "active";
                div[n].style.display = "block"; // 不是立即执行的 i可能与当前btn的不对应  所以用立即执行函数
            }
        }(i))
    }
</script>
<style>
    .content {
        width: 100px;
        height: 100px;
        border: 1px solid red;
        display: none;
    }
    .active {
        background-color: green;
    }
</style>
// 新建div 用定时器让它动起来
<script>
    var div = document.createElement('div'); // 新建个div,但此时在body里面看不到
    document.body.appendChild(div); // 把div推到body里面
    div.style.width = "100px";
    div.style.height = "100px";
    div.style.backgroundColor = "red";
    div.style.position = "absolute";
    div.style.left = "0";
    div.style.top = "0";
    // 动画
    //var speed = 1;
    //var timer = setInterval(function() {
    //    speed += speed/20;
    //    div.style.left = parseInt(div.style.left) + speed + "px";
    //    div.style.top = parseInt(div.style.top) + speed + "px";
    //    // 动画停止
    //    if(parseInt(div.style.top) > 200 && parseInt(div.style.left) > 200) {
    //        clearInterval(timer);
    //    }
    //}, 100);
    
    // 绑定小事件
    document.onkeydown = function(e) {
      switch(e.which) {
        case 38:  // 键盘上键
          div.style.top = parseInt(div.style.top) - 5 + "px";
          break;
        case 40:  // 键盘下键
          div.style.top = parseInt(div.style.top) + 5 + "px";
          break;
        case 37:  // 键盘左键
          div.style.left = parseInt(div.style.left) - 5 + "px";
          break;
        case 39:  // 键盘右键
          div.style.left = parseInt(div.style.left) + 5 + "px";
          break;
      }
    }
</script>
// 涂色
<ul>
    <li img-data="0"></li>
    ...........很多行
    <li img-data="0"></li>
    <li img-data="0"></li>
</ul>
<script>
    var ul = document.getElementsByTagName('ul')[0];
    ul.onmouseover = function(e) {
        var event = e || window.event;
        var target = event.target || event.srcElement;  // 考虑兼容性
        target.style.backgroundColor = "rgb(0, 255," + target.getAttribute('img-data') + ")";
        // getAttribute() 方法返回指定属性名的属性值
        // setAttribute() 方法添加指定的属性,并为其赋指定的值。如果这个指定的属性已存在,则仅设置/更改值。
        target.setAttribute('img-data', parseInt(target.getAttribute('img-data')) + 60);
    }
</script>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    ul {
        width: 400px;
        height: 400px;
        margin: 0 auto; /* 水平居中 */
        top: 50%;
        transform: translateY(50%); /* 垂直居中 */
    }
    li {
        box-sizing: border-box;   /* 盒模型, 虽然设置宽高10px,但因为有border,实际宽高8 */
        float: left;
        width: 10px;
        height: 10px;
        border: 1px solid black;
        list-style: none; /* 去掉li前面的小圆点 */
    }
</style>
<div>
	<strong></strong>    
</div>
<div>
	<span>
    	<strong class="demo">123</strong>
    </span>    
</div>
<script>
    // 缺点: 不实时
    // 返回的直接就是<strong class="demo">123</strong>
	var strong = document.querySelector('div > span strong.demo'); 
	var strong1 = document.querySelectorAll('div > span strong.demo'); // 返回的是类数组
</script>
遍历节点树
  • parentNode 父节点(最顶端的parentNode为#document)
  • childNodes 子节点们
  • firstChild 第一个子节点
  • lastChild 最后一个子节点
  • nextSibling 后一个兄弟节点
  • previousSibling 前一个兄弟节点
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
</div>
<script>
	var div = document.getElementsByTagName('div')[0];
    console.log(div.childNodes); // NodeList(7) [text, comment, text, strong, text, span, text]
    console.log(div.firstChild); // 123不管写多少与注释同算作文本节点-text
    console.log(div.lastChild); // #text  (没有内容是空格)
    console.log(div.firstChild.nextSibling); // <!-- comment -->
    console.log(div.lastChild.previousSibling); // <span></span>
</script>
基于元素节点树的遍历(没有文本节点,document等)
  • parentElement 返回当前元素的父元素节点(IE9及以下不兼容)
  • children 只返回当前元素的元素子节点
  • node.childElementCount === node.children.length 当前元素节点的子元素节点个数(IE9及以下不兼容)
  • firstElementChild 返回第一个元素节点(IE9及以下不兼容)
  • lastElementChild 返回最后一个元素节点(IE9及以下不兼容)
  • nextElementSibling 返回后一个兄弟元素节点(IE9及以下不兼容)
  • previousElementSibling 返回前一个兄弟元素节点(IE9及以下不兼容)
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
</div>
<script>
	var div = document.getElementsByTagName('div')[0];
    console.log(div.parentElement); // <body>....</body> ---> html ---> null
    console.log(div.children); //  HTMLCollection(2) [strong, span]
    console.log(div.childElementCount); // 2
    console.log(div.firstElementChild); // <strong></strong>
    console.log(div.lastElementChild); // <span></span>
    console.log(div.nextElementSibling); //  <scrip type="text/javascript">...</scrip>
    console.log(div.previousElementSibling); // null
</script>
节点类型
  • 元素节点 — 1(nodeType返回值)

  • 属性节点 — 2

  • 文本节点 — 3

  • 注释节点 — 8

  • document — 9

  • DocumentFragment — 11

    获取节点类型 — nodeType

节点的四个属性
  • nodeName 元素的标签名,以大写形式表示,只读

  • nodeValue text节点或comment节点的文本内容,可读写

  • nodeType 节点类型,只读

  • attributes element节点的属性集合

    节点的一个方法 Node.hasChildNodes()

<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
</div>
<script>
	var div = document.getElementsByTagName('div')[0];
    console.log(div.childNodes[0].nodeName); // #text
    console.log(div.childNodes[1].nodeName); // #comment
    console.log(div.childNodes[3].nodeName); // STRONG
    console.log(div.childNodes[0].nodeValue); // 123不管写多少与注释同算作文本节点-text
    console.log(div.childNodes[1].nodeValue); // comment
    console.log(div.childNodes[3].nodeValue); // null
    console.log(div.childNodes[0].nodeType); // 3 -- 文本节点 
    console.log(div.childNodes[1].nodeType); // 8 -- 注释节点
    console.log(div.childNodes[3].nodeType); // 1 -- 元素节点
</script>
<div id="only" class="one"></div>
<script type="text/javascript">
    var div = document.getElementsByTagName('div')[0];
	console.log(div.attributes); // NamedNodeMap {0: id, 1: class, id: id, class: class, length: 2}
    console.log(div.attributes[0]);  // id="only"
    console.log(div.attributes[0].nodeType); // 2 
    console.log(div.attributes[0].value); // only  可以更改
    console.log(div.attributes[0].name); // id  不可更改
    console.log(div.hasChildNodes()); // false
</script>
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
</div>
<script>
    // 返回所有的元素节点
    function retElementChild(node) {
        var temp = {
            length: 0,
            push: Array.prototype.push, // 不加splice返回: {0: strong, 1: span, length: 2, push: ƒ}
            splice: Array.prototype.splice // 让类数组的样式看起来更像数组
        },
            child = node.childNodes,
            len = child.length;
        for(var i = 0; i < len; i++) {
            if(child[i].nodeType === 1) {
                temp.push(child[i]);
            }
        }
        return temp;
    }
    console.log(retElementChild(div)); // Object(2) [strong, span, push: ƒ, splice: ƒ]
</script>
DOM结构树(继承关系)
	 |----Document --- HTMLDocument         |--- HTMLHeadElement
	 |							            |
	 |						                |--- HTMLBodyElement
	 |						|--Text		    |
	 |--- CharacterData --- |				|--- HTMLTitleElement
Node-|						|--Comment		|
	 |										|--- HTMLParagraphElement
	 |										|
	 |----Element --- HTMLElement -------	|--- HTMLInputElement
	 |           							|
	 |										|--- HTMLTableElement
	 |										|
	 |--- Attr								|--- .etc

document.__proto__  										---> HTMLDocument
document.__proto__.__proto__   								---> Document {…}
document.__proto__.__proto__.__proto__   					---> Node {…}
document.__proto__.__proto__.__proto__.__proto__   			---> EventTarget
document.__proto__.__proto__.__proto__.__proto__.__proto__  ---> Object {...constructor: ƒ,…}
document.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__  ---> null

1、getElementById方法定义在Document.prototype上,即Element节点上不能使用

2、getElementsByName方法定义在HTMLDocument.prototype上,即非html中的document不能使用(xml document、Element)

3、getElementsByTagName方法定义在Document.prototype和Element.prototype上

4、HTMLDocument.prototype定义了一些常用的属性,body,head分别指代HTML文档中的标签

5、Document.prototype定义了documentElement属性,指代文档的根元素,在HTML文档中,指代标签

6、getElementsByClassName、querySelectorAll、querySelector在Document.prototype,Element.prototype类中均有定义

作业:
1、遍历元素节点树(在原型链上编程)
2、封装函数,返回元素e的第n层祖先元素节点
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
    <em></em>
    <i></i>
    <b></b>
</div>
<script>
    function retParentElementNode(elem, n) {
        while(elem && n) {
            elem = elem.parentElement;
            n--;
        } 
        return elem;
    }
    var em = document.getElementsByTagName('em')[0];
    console.log(retParentElementNode(em, 2)); // <body>...</body>
    console.log(retParentElementNode(em, 3)); // <html>...</html>
    console.log(retParentElementNode(em, 4)); // null
    console.log(retParentElementNode(em, 0)); // <em></em>
    console.log(retParentElementNode(em, -1)); // null
</script>
3、封装函数,返回元素e的第n个兄弟元素节点,n为正,返回后面的兄弟元素节点;n为负,返回前面的;n为0,返回自己
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span></span>
    <em></em>
    <i></i>
    <b></b>
    <!-- comment -->
</div>
<script>
    function retSibling(elem, n) {
        while(elem && n) {
            if(n > 0) {
                if(elem.nextElementSibling) {
                    elem = elem.nextElementSibling; // IE9以下不兼容
                } else {
                    // for(elem = elem.nextSibling;elem && elem.nodeType != 1;elem = elem.nextSibling);
                    do {
                        elem = elem.nextSibling;
                    } while(elem && elem.nodeType != 1)
                }
                n--;
                } else {
                    if(elem.previousElementSibling) {
                        elem = elem.previousElementSibling; // IE9以下不兼容
                    } else {
                // for(elem = elem.previousSibling;elem && elem.nodeType != 1;elem = elem.previousSibling);
                        do {
                            elem = elem.previousSibling;
                        } while(elem && elem.nodeType != 1)
                    }
                    n++;
                    }
                }
                return elem;
            }
            var em = document.getElementsByTagName('em')[0];
            console.log(retSibling(em, -300)); // null
            console.log(retSibling(em, -3)); // null
            console.log(retSibling(em, -2)); // <strong></strong>
            console.log(retSibling(em, 0)); // <em></em>
            console.log(retSibling(em, 2)); // <b></b>
            console.log(retSibling(em, 3)); // null
            console.log(retSibling(em, 300)); // null
</script>
4、编辑函数,封装myChildren功能,解决以前部分浏览器的兼容性问题(子元素节点,实现children功能)
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span>
        <i></i>
        <strong>
            <i></i>
        </strong>
    </span>
    <em>...</em>
    <b></b>
</div>
<script>
    // 原型链上封装
    Element.prototype.myChildren =  function() {
        var child = this.childNodes,
            len = child.length,
            arr = [];
        for(var i = 0; i < len; i++) {
            if(child[i].nodeType == 1) {
                arr.push(child[i]);
            }
        }
        return arr
    }
    var span = document.getElementsByTagName('span')[0];
    var em = document.getElementsByTagName('em')[0];
    console.log(span.myChildren()); // [i, strong]
    console.log(em.myChildren()); // []
</script>
5、封装hasChildren()方法,不可用children属性(hasChildNodes方法)
<div>
    123不管写多少与注释同算作文本节点-text
    <!-- comment -->
    <strong></strong>
    <span>
        <i></i>
    </span>
    <em>...</em>
    <b></b>
</div>
<script>
    function hasChildren(elem) {
        var child = elem.childNodes,
            len = child.length;
        for(var i = 0; i < len; i++) {
            if(child[i].nodeType == 1) {
                return true;
            }
        }
        return false;
    }
    var span = document.getElementsByTagName('span')[0];
    var em = document.getElementsByTagName('em')[0];
    var b = document.getElementsByTagName('b')[0];
    console.log(hasChildren(span)); // true
    console.log(hasChildren(em)); // false
    console.log(hasChildren(b)); // false
</script>

  • document.createElement(); 创建元素节点
  • document.createTextNode(); 创建文本节点
  • document.createComment(); 创建注释节点
  • document.createDocumentFragment(); 创建文档碎片节点

  • parentNode.appendChild(); 剪切操作:之前在一个位置,下次添加到另一个位置,则原位置的就没了

    ------ document.body.appendChild(div);

  • parentNode.insertBefore(a, b); 在b之前插入a

  • parent.removeChild(); 删除子元素
  • child.remove(); es5新方法,删除自身

替换

  • parent.replaceChild(new, origin);

element节点的一些 属性 方法

dom.className可以读写class

属性
  • innerHTML — div.innerHTML 获取当前div的HTML结构 div.innerHTML = "…"写HTML结构,原来的结构被覆盖

    div.innerHTML = "123"也可以生效

  • innerText 火狐不兼容,火狐用textContent,但是老IE不兼容

方法
  • ele.setAttribute(‘属性名’, ‘属性值’); div.setAttribute(‘class’, ‘demo’) ---->
  • ele.getAttribute(); div.getAttribute(‘class’); —> demo
作业:
1、用js脚本生成下面的DOM结构。

使用标准的DOM属性或方法。

<div class="example">
    <p class="slogan">
        hahahahhahahahaha
    </p>
</div>
// 
<script>
    var div = document.createElement('div');
    document.body.appendChild(div);
    div.setAttribute('class', 'example');
    var p = document.createElement('p');
    div.appendChild(p);
    p.setAttribute('class', 'slogan');
    p.innerHTML = 'hahahahhahahahaha';
</script>
2、封装函数insertAfter(),功能类似insertBefore()

可忽略老版本浏览器,直接在Element.prototype上编程

<div>
    <span></span>
    12233
    <!--jajhaghq-->
    <em></em>
    <strong></strong>
    <i></i>
</div>
<script>
    // 在b之后插入a
    Element.prototype.insertAfter = function(a, b) {
        var beforeNode = b.nextElementSibling;
        if(beforeNode) {
            // b.parentElement.insertBefore(a, beforeNode);
            this.insertBefore(a, beforeNode); // 在原型链上编程this可以指代任何要调用的对象,直接封装function,this要变成一个参数
        } else {
            this.appendChild(a);
        }
    }
    var span = document.getElementsByTagName('span')[0];
    var p = document.createElement('p');
    div.insertAfter(p, span);
</script>
3、将目标节点内部的节点顺序逆序

eg:

---->

<div>
    <span>
        <strong></strong>
        <em></em>
    </span>
    <b></b>
    <strong></strong>
    <i></i>
    <em></em>
</div>
<script>
    function reverse(elem) {
        var chi = elem.children,
            result = [],
            len = chi.length;
        for(var i = 0; i < len/2; i++) {
            result[i] = chi[len-1-i];
            result[len-1-i] = chi[i];
        }
        return result;
    }
    var div = document.getElementsByTagName('div')[0];
    var span = document.getElementsByTagName('span')[0];
    console.log(reverse(div));  // [em, i, strong, b, span]
    console.log(reverse(span)); // [em, strong]
</script>
// appendChild
<script>
function reverse(elem) {
    var chi = elem.children,
        len = chi.length,
        length = 0;
    if(len % 2 == 0) {
        length = len/2;
    } else {
        length = len/2 + 1;
    }
    for(var i = 0; i < length; i++) {
        elem.appendChild(chi[len-2-i]);
    }
    return chi;
}
</script>

17 Date()日期对象

作业

1、封装函数,打印当前的年月日时分秒

function format(num) {
    num = num < 9 ? '0' + num : num;
    return num;
}
var nowDate = new Date();
var year = nowDate.getFullYear();
var month = nowDate.getMonth()+1;
var day = nowDate.getDate();
var hour = nowDate.getHours();
var min = nowDate.getMinutes();
var second = nowDate.getSeconds();
var date = year+':'+format(month)+':'+format(day) +' '+format(hour)+':'+format(min)+':'+format(second);
console.log(date);

18 定时器

  • setInterval(function(){},1000),其返回值作为定时器的唯一标识,在凊除定时器时传唯一标识就行,一般定义一个变量接收唯一标识

    第一个参数可以不传函数,直接传字符串也可以执行,比如:setInterval(“console.log(‘a’)”,1000)

  • setTimeout()只执行一次,同时出现,唯一标识不会与setInterval重复

  • clearInterval()

  • clearTimeout()

全局对象window上的方法,内部函数this指向window

作业

1、写个计时器,3分钟停止

<style>
    input {
        border: 1px solid rgb(0,0,0,0.8);
        text-align: right;
        font-size: 16px;
        font-weight: bold;
    }
</style>
minutes: <input type="text" value="0" />
seconds: <input type="text" value="0" />
<script>
    var input1 = document.getElementsByTagName('input')[0];
    var input2 = document.getElementsByTagName('input')[1];
    var minutes = 0,
        seconds = 0;
    var timer = setInterval(function() {
        seconds++;
        if(seconds == 60) {
            minutes++;
            seconds = 0;
        }
        if(minutes == 3) {
            clearInterval(timer);
        }
        input1.setAttribute('value', minutes); // input1.value = minutes;
        input2.setAttribute('value', seconds); // input2.value = seconds;
    }, 1000)
</script>

19 dom基本操作

查看滚动条的滚动距离

  • window.pageXOffset/pageYOffset (IE8及以下不兼容)
  • document.body.scrollLeft/scrollTop
  • document.documentElement.scrollLeft/scrollTop (兼容性混乱,用时与document.body.scrollLeft/scrollTop相加,因为不可能存在两个同时有值)
作业

1、封装兼容性方法getScrollOffset(),求滚动距离

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
        }
    }
}

查看视口的尺寸

  • window.innerWidth/innerHeight (IE8及以下不兼容)
  • document.documentElement.clientWidth/clientHeight (标准模式下,任意浏览器都兼容)
  • document.body.clientWidth/clientHeight (适用于怪异模式下的浏览器,去掉)
作业

1、封装兼容性方法getViewportOffset(),返回浏览器视口尺寸

查看当前浏览器模式document.compatMode CSS1Compat标准模式 BackCompat怪异模式

function getViewportOffset() {
    if(window.innerWidth) {
        return {
            width: window.innerWidth,
            height: window.innerHeight
        }
    } else {
        if(document.compatMode === 'CSS1Compat') {
            return {
                width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight
            }
        } else {
            return {
                width: document.body.clientWidth,
                height: document.body.clientHeight
            }
        }
    }
}

查看元素的几何尺寸

  • domElement.getBoundingClientRect() es5新方法 兼容性好,返回对象,里面有left、top、right、bottom等属性。

left,top代表该元素左上角的X和Y坐标,right和bottom代表元素右下角的X和Y坐标

width和height属性老版本IE并未实现,返回的是加上padding之后的宽高,不包括margin,非内容的宽高

返回结果非实时

查看元素的尺寸

  • dom.offsetWidth/offsetHeight 返回的是加上padding之后的宽高,不包括margin,非内容的宽高

查看元素的位置

  • dom.offsetLeft/offsetTop

对于无定位父级的元素,返回相对文档的坐标。对于有定位父级的元素,返回相对于最近的有定位的父级的坐标

  • dom.offsetParent

返回最近的有定位的父级,如无则返回body,

body.offsetParent 返回null

作业

1、求元素相对于文档的坐标getElementPosition()

function getElementPosition(elem) {
    var xoffset = 0, yoffset = 0;
    while(elem.offsetParent) {
        xoffset += elem.offsetLeft;
        yoffset += elem.offsetTop;
        elem = elem.offsetParent;
    }
    return {
        x: xoffset,
        y: yoffset
    }
}

让滚动条滚动

  • scroll(x, y)
  • scrollTo(x, y)
  • scrollBy(x, y)

三个方法功能类似,用法都是将x,y坐标传入。让滚动条滚动到当前位置

区别: scrollBy会在之前的数据上累加

作业

1、利用scrollBy实现快速阅读的功能

<div>.........................</div>
<div class="demo" style="background-color: red;bottom: 190px;">start</div>
<div class="demo" style="background-color: blue;bottom: 120px;">加速</div>
<div class="demo" style="background-color: green;bottom: 50px;">stop</div>
<script>
    var start = document.getElementsByTagName('div')[1];
    var speed = document.getElementsByTagName('div')[2];
    var stop = document.getElementsByTagName('div')[3];
    var timer = 0, sp = 10,key = true;
    start.onclick = function () {
        if(key) {
            timer = setInterval(function () {
                window.scrollBy(0, sp);
            }, 100);
            key = false;
        }
    }
    speed.onclick = function() {
        sp += 10;
    },
        stop.onclick = function() {
        clearInterval(timer);
        key = true;
    }
</script>

20 脚本化CSS

读写元素css属性

  • dom.style.prop
    • 可读写行间样式,没有兼容性问题,碰到float这样的保留字属性,前面应加css。eg: float —> cssFloat
    • 复合属性必须拆解border,font,组合单词变成小驼峰式写法background-color —> backgroundColor
    • 写入的值必须是字符串格式

查询计算样式

  • window.getComputedStyle(ele, null); 获取到的是显示出来的值或者默认值,多次赋值只显示最后成功的那个值
    • 计算样式只读
    • 返回的计算样式的值都是绝对值,没有相对单位
    • IE8及以下不兼容
<style>
    /* 伪元素 */
    div::after {
        content: '';
        width: 50px;
        height: 50px;
        background-color: pink;
        display: inline-block;
    }
    .yellow::after {
        background-color: yellow;
    }
    .green::after {
        background-color: green;
    }
</style>
<div style="height: 100px;background-color: red;"></div>
<script>
    var div = document.getElementsByTagName('div')[0];
    // 唯一获取伪元素的样式表方法
	window.getComputedStyle(div, 'after');
    // 改变伪元素的颜色
    var count = 0;
    div.onclick = function() {
        if(count % 2 == 0) {
            div.className = 'yellow';
        } else {
            div.className = 'green';
        }
        count++;
    }
</script>

查询样式

  • ele.currentStyle
    • 计算样式只读
    • 返回的计算样式的值不是经过转换的绝对值
    • IE独有的特性

作业

1、封装兼容性方法getStyle(elem, prop); 查询计算样式

function getStyle(elem, prop) {
    if(window.getComputedStyle) {
        return window.getComputedStyle(elem, null)[prop];
    } else {
        return elem.currentStyle[prop];
    }
}

2、方块运动

<div style="width: 100px;height: 100px;background-color: red;position: absolute;left: 0;"></div>
<script>
    function getStyle(elem, prop) {
        if(window.getComputedStyle) {
            return window.getComputedStyle(elem, null)[prop];
        } else {
            return elem.currentStyle[prop];
        }
    }
    var div = document.getElementsByTagName('div')[0];
    var speed = 1;
    var timer = setInterval(function() {
        speed += speed/10;
        div.style.left = parseInt(getStyle(div, 'left')) + speed + 'px';
        if(parseInt(div.style.left) > 500) {
            clearInterval(timer);
        }
    }, 10);
</script>

3、轮播图

21 事件

交互体验的核心功能

演示demo 拖拽和点击

如何绑定事件处理函数

  • 1、ele.onxxx = function (event) {}

    • 兼容性好,但是一个元素的同一个事件上只能绑定一个处理程序
    • 基本等同于写在HTML行间上 ----- onxxx=“函数里面的语句”
  • 2、obj.addEventListener(type事件类型, fn处理函数, false);

    div.addEventListener('click', function() {
        console.log('a');
    }, false);
    div.addEventListener('click', function() {
        console.log('a');
    }, false);   // 最后会输出两个a
    // 或者
    div.addEventListener('click', test, false);  // 第二个参数是函数引用
    div.addEventListener('click', test, false);  // 最后只会输出一个a
    function test() {
        console.log('a');
    }
    
    • IE9以下不兼容,可以为一个事件绑定多个处理函数(处理函数都用test表示,则只执行一次,除非写成test函数体,才会执行两次)
  • 3、obj.attachEvent(‘on’ + type, fn);

    • IE独有,一个事件同样可以绑定多个处理函数(同一个函数绑定多次会执行多次)
作业

1、使用原生js, addEventListener,给每个li元素绑定一个click事件,输出它们的顺序

<ul>
    <li>a</li>
    <li>a</li>
    <li>a</li>
    <li>a</li>
</ul>
<script>
    var lis = document.getElementsByTagName('li');
    for(var i = 0; i < lis.length; i++) { 
        // 绑定事件注意是否形成闭包
        (function (j) {
            lis[j].addEventListener('click', function() {
                console.log(j);
            }, false);
        })(i);
    }
</script>

事件处理程序的运行环境

  • 1、ele.onxxx = function(event) {}
    • 程序this指向是dom元素本身
  • 2、obj.addEventListener(type事件类型, fn处理函数, false);
    • 程序this指向是dom元素本身
  • 3、obj.attachEvent(‘on’ + type, fn);
    • 程序this指向window
作业

1、封装兼容性的addEvent(elem, type, handle);方法 绑定事件处理函数

function addEvent(elem, type, handle) {
    if(elem.addEventListener) {
        elem.addEventListener(type, handle, false);
    } else if(elem.attachEvent) {
        elem.attachEvent('on' + type, function() {
            handle.call(elem); // 改变this指向为elem
        });
    } else {
        elem['on' + type] = handle;
    }
}

解除事件处理程序

  • ele.onclick = false null ‘’
  • ele.removeEventListener(type, fn, false);
  • ele.detachEvent(‘on’ + type, fn);

若绑定匿名函数,则无法解除

封装removeEvent:

function removeEvent(elem, type, handle) {
    if(elem.removeEventListener) {
        elem.removeEventListener(type, handle, false);
    } else if(elem.detachEvent) {
        elem.detachEvent('on' + type, function() {
            handle.call(elem);
        });
    } else {
        elem['on' + type] = null;
    }
}

事件处理模型—事件冒泡、事件捕获

  • 事件冒泡

    • 结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(自底向上)
  • 事件捕获(addEventListener的第三个参数为true; div.setCapture(); 捕获页面上发生的所有事件算是自己的 div.releaseCapture(); )

    • 结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素)。(自顶向下)
    • IE没有捕获事件
  • 触发顺序

    • 先捕获,后冒泡
  • focus、blur、change、submit、reset、select等事件不冒泡

<div class="wrapper">
    <div class="content">
        <div class="box"></div>
    </div>
</div>
<script>
    var wrapper = document.getElementsByClassName('wrapper')[0];
    var content = document.getElementsByClassName('content')[0];
    var box = document.getElementsByClassName('box')[0];
    // 点击box时,依次打印出     box冒泡、content冒泡、wrapper冒泡(结构上自底向上冒泡)
    wrapper.addEventListener('click', function() {
        console.log('wrapper冒泡');
    }, false);
    content.addEventListener('click', function() {
        console.log('content冒泡');
    }, false);
    box.addEventListener('click', function() {
        console.log('box冒泡');
    }, false);
    // 事件捕获   addEventListener的第三个参数变为true
    // 点击box时,依次打印出 wrapper捕获、content捕获、box捕获(结构上自顶向下捕获)
    wrapper.addEventListener('click', function() {
        console.log('wrapper捕获');
    }, true);
    content.addEventListener('click', function() {
        console.log('content捕获');
    }, true);
    box.addEventListener('click', function() {
        console.log('box捕获');
    }, true);
    // 整体打印出     wrapper捕获、content捕获、box捕获、box冒泡、content冒泡、wrapper冒泡
    // 火狐打印结果:  wrapper捕获、content捕获、box冒泡、box捕获、content冒泡、wrapper冒泡
    // 点击box,box此时是事件执行,先绑定的先执行。box冒泡先绑定的应该先输出,再是box捕获
</script>

取消冒泡、阻止默认事件

  • 取消冒泡

    • W3C标准event.stopPropagation();但不支持IE9以下版本
    • IE独有 event.cancelBubble = true; 谷歌也实现了
  • 阻止默认事件

    • 默认事件 — 表单提交, a标签跳转,右键菜单等. 右键出菜单document.oncontextmenu
    • 1、return false; 以对象属性的方式注册的事件才生效eg: div.οnclick=function(){}
    • 2、event.preventDefault(); W3C标注,IE9以下不兼容
    • 3、event.returnValue = false; 兼容IE
// a标签取消跳转
<a href="javascript:void(false)">demo</a>
// void(false)就相当于return false
作业

1、封装取消冒泡的函数 stopBubble(event)

function stopBubble(event) {
    if(event.stopPropagation) {
        event.stopPropagation();
    } else {
        event.cancelBubble = true;
    }
}

2、封装阻止默认事件的函数cancelHandler(event);

function cancelHandler(event) {
    if(event.preventDefault) {
        event.preventDefault();
    } else {
        event.returnValue = false;
    }
}

事件源对象

事件对象:event || window.event 用于IE

事件源对象: 在点击事件时可以得出点击的是哪个

  • event.target 火狐只有这个
  • event.srcElement IE只有这个
  • chrome都有

事件委托

利用事件冒泡和事件源对象进行处理

优点:

1、性能 不需要循环所有的元素一个个绑定事件

2、灵活 当有新的子元素时不需要重新绑定

作业

1、 点击每个li时打印出里面的值

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);
}

2、拖拽方块

function drag(elem) {
  var disX, disY;
  addEvent(elem, 'mousedown', function(e) {
    var event = e || window.event;
    disX = event.clientX - parseInt(getStyle(elem, 'left'));
    disY = event.clientY - parseInt(getStyle(elem, 'top'));
    addEvent(document, 'mousemove', mouseMove);
    addEvent(document, 'mouseup', mouseUp);
    stopBubble(event);
    cancelHandler(event);
  });
  function mouseMove(e) {
    var event = e || window.event;
    elem.style.left = event.clientX - disX + 'px';
    elem.style.top = event.clientY - disY + 'px';
  }
  function mouseUp(e) {
    var event = e || window.event;
    removeEvent(document, 'mousemove', mouseMove);
    removeEvent(document, 'mouseup', mouseUp);
  }
}

事件分类

鼠标事件:

click、mousedown、mousemove鼠标移动、mouseup、contextmenu右键出菜单、mouseover、mouseout、mouseenter(h5新规范)、mouseleave(h5新规范)

移动端:touchstart touchmove touchend

DOM3标准规定: click事件只能监听左键,只能通过mousedown和mouseup来判断鼠标键。

用e.button来区分鼠标的按键: 0-左键 1-滚轮 2-右键

// 区分鼠标按键
document.onmousedown = function(e) {
  console.log(e.button);
}
document.onmouseup = function(e) {
  console.log(e.button);
}

如何解决mousedown和click的冲突: click = mousedown + mouseup

区分是拖拽还是点击,看mousedown与mouseup的时间差:

var firstTime = 0, lastTime = 0, 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;
  }
}
键盘事件:

keydown > keypress > keyup 顺序

keydown 与 keypress 的区别:

  • keydown 可以响应任意键盘按键, keypress只可以响应字符类键盘按键
  • keypress返回ASCII码,可以转换成相应字符( String.formCharCode(e.charCode) )
文本操作事件:

input、focus、blur、change

input.oninput 只要文本框里面值发生变化就触发,不用等失焦

input.onchange 只有失焦之后且文本框前后值发生变化才触发

窗体操作类(window上的事件):

scroll load

作业

1、fixed定位 js兼容版

2、完善轮播图,加按钮 正反播放

3、提取密码框的密码 监听键盘事件

4、输入框功能完善

5、二级菜单栏

6、贪吃蛇

7、扫雷

22 json

json是一种传输数据的格式(以对象为样板,本质上就是对象,但用途有区别,对象是本地用的,json使用来传输的)

JSON.parse(); string ----> json

JSON.stringify(); json ----> string

页面渲染:

dom树 + css树 = renderTree

renderTree会发生 重排reflow (效率低) 或者 重绘repaint

reflow: dom节点的删除、添加

​ dom节点的宽高变化、位置变化, diaplay:none —> block等

​ offsetWidth offsetLeft

repaint:css背景图片改变,颜色变化等

异步加载js

js加载缺点: 加载工具方法没必要阻塞文档,js加载太过会影响页面效率,一旦网速不好,整个网站将等待js加载而不进行后续渲染等工作

有些工具方法需要按需加载,用到时加载,不用不加载

异步加载的三种方案:

  • defer 异步加载,要等到dom文档全部解析完才会被执行。只有IE能用,也可以将代码写到内部<script defer>代码</script>
  • async 异步加载,加载完就执行,async只能加载外部脚本,不能把js写在script标签里<script async src="xxx.js"></script>
    • defer、async执行时也不阻塞页面
  • 3、创建script,插入到DOM中,加载完毕后callBack回调函数
function loadScript预解析时不会执行函数里面的代码,然后解析(url, callback) {
  var script = document.createElement('script');
  script.type = "text/javascript";
  if(script.readyState) {
    // IE方法
    script.onreadystatechange = function() {
      if(script.readyState == "complete" || script.readyState == "loaded") {
        callback();
      }
    }
  } else {
    script.onload = function() { // IE无load函数
      callback();   // 下载的url文件里面的函数,等到文件加载完再执行
    }
  }
  script.src = url; // 只是下载  先绑定事件再加载,防止已经加载完毕readyState已经从loading变为loaded,此时readystatechange不会执行,后面的就不会执行了
  document.head.appendChild(script); // 推到head或body等才会开始执行
}
loadScript('xxx.js', test);// 直接写函数名会报错test未定义,因为function loadScript预解析时不会执行函数里面的代码,然后解析loadScript('xxx.js', test);就会报错
loadScript('xxx.js', function() {
  test();   // 这样写预解析时不会执行test,正式执行时上面的也已经加载完成了,不会报错
});
loadScript('xxx.js', "test()") // 这样写的话callback()---->  eval(callback)  也可以执行
// eval把字符串当做代码来执行
loadScript('xxx.js', "test");  // callback()----> tools[callback]();
// xxx.js中函数需按照一定的格式写
var tools = {
  test: function() {...},
  demo: function() {...}
}

js加载时间线

  • 1、创建document对象,开始解析web页面。document.readyState =“loading” 解析HTML元素和文本内容后添加element对象和text节点到文档中
  • 2、遇到link外部 css,创建线程加载,并继续解析文档
  • 3、遇到script外部js 无defer、async,浏览器加载,并阻塞,等js加载完成并执行该脚本,然后继续解析文档
  • 4、遇到script外部js 有defer、async,浏览器创建线程加载,并继续解析文档。async脚本加载完之后立即执行,defer等js加载完成之后执行(异步禁止使用document.write(),等js加载完成之后即load中执行document.write,里面的代码会覆盖之前的页面)
  • 5、遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档。
  • 6、当文档解析完成,document.readyState =“interactive”
  • 7、文档解析完成后,所有设置defer的脚本按照顺序执行。
  • 8、document对象触发DOMContentLoaded事件,程序执行从“同步脚本执行阶段”转为”事件驱动阶段“
  • 9、当所有async脚本加载完成并执行后,img等加载完成后,document.readyState =“complete”,window对象触发load事件
  • 10、以异步响应方式处理用户输入、网络事件等。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script>
    // 模拟jquery的$(document).ready方法
    document.addEventListener('DOMContentLoaded', function() {
      var div = document.getElementsByTagName('div')[0];
    }, false);
  </script>
</head>
<body>
  <div style="width: 100px;height: 100px;background-color: greenyellow;"></div>
  <script>
    // 执行结果
   // loading --- Uncaught ReferenceError: $ is not defined  ---  interactive ---  a  ---  complete
    console.log(document.readyState); // loading
    document.onreadystatechange = function() {
      console.log(document.readyState);  // 先interactive   再 complete  
    }
    document.addEventListener('DOMContentLoaded', function() {// 只能采用addEventListener方法,document.onxxx方法不可取
      console.log('a'); // 先于complete执行,文档解析完成出现
    }, false);
    // 当dom解析完就执行的部分    jquery方法
    $(document).ready(function() {
      console.log('b');   // dom解析完就执行,不必等到load阶段
    })  
  </script>
</body>
</html>

23 正则表达式

转义字符 \

字符串换行符 \n

\r 行结束符

\t table一个缩进

正常情况一个回车是\r\n,linux系统里回车是\n

多行字符串,在每行末尾加\

RegExp正则表达式作用: 匹配特殊字符或有特殊搭配原则的字符的最佳选择

两种创建方式

  • 直接量
  • new RegExp(“字符串”, “类型gim等”);

/…/img i: ignoreCase 忽略大小写 m: 多行匹配 g: 全局匹配(查找所有匹配而非在找到第一个匹配后停止)

^以后面的字符开头,但是放在表达式[]里面表示 “非”

var reg = /abc/m;
var reg1 = new RegExp(reg);
var reg2 = RegExp(reg);
// reg1与reg不指向同一个引用, reg.abc = 123;reg1.abc = undefined;
// reg2与reg指向同一个引用, reg.abc = 123;reg2.abc = 123;

// 全局匹配
var reg = /ab/;
var reg1 = /ab/g;
var str = 'abababababab';
str.match(reg); // ["ab"]       match返回匹配的字符
reg.test(str); // true          test返回匹配结果
str.match(reg1); // ["ab", "ab", "ab", "ab", "ab", "ab"]
// 多行匹配
var reg = /^a/g;
var reg1 = /^a/gm;
var str = 'abcd\na';   // \n换行,所有有两个以a开头的字段
str.match(reg); // ["a"]  虽然换行了,但未多行匹配,所以只能输出一个a
str.match(reg1);  //  ["a", "a"]  

[]

[可以取的范围]表达式, 查找某个范围内的字符。一个[]代表一位

[adgk] 查找给定集合内的任何字符

[^adgk] 查找给定集合外的任何字符

(abc|bcd|cde) 查找任何指定的选项 — str是abc,bcd,cde都可以返回true,|表示或

var reg = /(abc|bcd)[0-9]/g;
var str = 'abcd3abc5';
str.match(reg); //  ["bcd3", "abc5"]

var reg = /([a-z]|[0-9])[0-9]/g;    // 等同于  var reg = /[a-z0-9][0-9]/g;
var str = 'abcd3abc587345';
str.match(reg); // ["d3", "c5", "87", "34"]

元字符

拥有特殊含义的字符

元字符描述
\d查找数字。[0-9]
\D[^\d]
\s查找空白字符。[\t\r\n\v\f ] 空格符在reg里面直接空格就行、制表符\t、回车符\r、换行符\n、垂直换行符\v、换页符\f
\S查找非空白字符。[ ^\s]
\b匹配单词边界。 str = ‘abc bcd cde’ 有六个单词边界a,c,b,d,c,e。reg=/\bcde/g; match出cde
\w[0-9A-z_]
\W[^\w]
.查找单个字符,除了换行和行结束符。[ ^\r\n]
\0查找NUL字符
\xxx查找以八进制数 xxx 规定的字符。
\xdd查找以十六进制数 dd 规定的字符。
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符。

量词

量词描述
n+匹配任何包含至少一个 n 的字符串。 {1, 无穷} 贪婪匹配 能多不少 在量词后面加?即 n+? 变成非贪婪匹配,能少不多
n*匹配任何包含零个或多个 n 的字符串。 {0, 无穷} n*?能匹配0个不取多
n?匹配任何包含零个或一个 n 的字符串。 {0, 1} n??能匹配0个不取多
n{x}{x}
n{x, y}{x, y} n{x, y}? 能取x个不取x之后到y的
n{x,}{x, 无穷}
n$匹配任意结尾为 n 的字符串
^n匹配任意开头为 n 的字符串
?=n匹配任何其后紧接指定字符串 n 的字符串 正向预查
?!n匹配任何其后没有紧接指定字符串 n 的字符串 非正向预查
var reg = /\w+/g;
var reg1 = /\w*/g;
var reg2 = /\w?/g;
var str = "abc";
str.match(reg);  // ["abc"]
str.match(reg1);  // ["abc", ""]   匹配到abc后,光标在c之后,也会匹配一个逻辑上的空。如果reg1为/\d*/g,会匹配出四个""
str.match(reg2);  // ["a", "b", "c", ""]
"abacaa".match(/a(?=b)/g);  // ["a"]  正向预查  正向断言    只找a后面跟着b的a
"abacaa".match(/a(?!b)/g); // ["a", "a", "a"]  非正向预查  只找a后面不跟着b的a
作业:字符串首尾是否含有数字
// 字符串首尾是否含有数字
var reg = /^\d|\d$/g;
var str = "1..,kiuyg3";
str.match(reg); // ["1", "3"]
// 字符串首尾是否都含有数字,中间匹配任意字符0或多个
var reg = /^\d[\s\S]*\d$/g;

RegExp对象方法

test: 检索字符串中指定的值。返回true 或 false

exec: 检索字符串中指定的值。返回找到的值,并确定其位置。index返回的是第一个匹配字符的位置

compile: 编译正则表达式

var reg = /ab/g;
var str = "ababab";  // 0a1b 2a3b 4a5b6 加位置
console.log(reg.lastIndex); // 0  游标位置。exec通过游标位置找下一次匹配。改变reg.lastIndex=2的值,exec值也会相应变化
reg.exec(str);     // ["ab", index: 0, input: "ababab", groups: undefined] 类数组
console.log(reg.lastIndex);  // 2
reg.exec(str);     // ["ab", index: 2, input: "ababab", groups: undefined]
console.log(reg.lastIndex);  // 4
reg.exec(str);     // ["ab", index: 4, input: "ababab", groups: undefined]
console.log(reg.lastIndex);  // 6
reg.exec(str);     // null
reg.exec(str);     // ["ab", index: 0, input: "ababab", groups: undefined]
reg.compile(str)// /ababab/
作业:匹配形如aabb,aaaa格式的字符串
// 匹配四个一样字符的字符串
var reg = /(\w)\1\1\1/g;  // \1反向引用第一个子表达式里面匹配的内容
var str = "aaaaccccggffff";
str.match(reg); //  ["aaaa", "cccc", "ffff"]
// 匹配形如aabb,aaaa格式的字符串
var reg = /(\w)\1(\w)\2/g;  // \2反向引用第二个子表达式里面匹配的内容
var str = "aaaaccccggffff";
str.match(reg); // ["aaaa", "cccc", "ggff"]

var reg = /(\w)\1(\w)\2/g;  // \2反向引用第二个子表达式里面匹配的内容
var str = "aabb";
str.match(reg);  // ["aabb"]
reg.exec(str);   // ["aabb", "a", "b", index: 0, input: "aabb", groups: undefined]
// 多出来的a,b分别是第一个与第二个子表达式匹配的内容

string对象的方法

match: 找到一个或多个正则表达式的匹配。正则表达式不加g,返回结果同exec的格式;加g只返回匹配值无其他index信息等

search: 检索与正则表达式相匹配的值,返回第一个匹配值的位置。接受字符串作为搜索参数。字符串参数将被转换为正则表达式

replace: 替换与正则表达式匹配的子串,返回正则表达式被替换处修改后的字符串。

split: 把字符串分割为字符串数组,返回不包括正则表达式后的切割字符串数组

var reg = /ab/g;
var str = "ababab";
str.search(reg); // 0  返回第一个匹配值的位置。匹配不到返回-1
str.search("b"); // 1
str.replace(reg, 'cd'); // "cdcdcd"   如果正则表达式不带g,也只能替换第一个
str.split(reg);  // ["", "", "", ""]
"abcabdaabe".split(reg);  // ["", "c", "da", "e"]
 
// 普通的replace不能替换全局,只会替换第一个
var str = "ababab";
str.replace("ab", 'cd');  // "cdabab"
作业: aabb 变为 bbaa
// aabb 变为  bbaa
var reg = /(\w)\1(\w)\2/g;  
var str = "aabb";
str.replace(reg, "$2$2$1$1"); // "bbaa"   $反向引用第几个表达式的内容,如果想替换成$,需要写成$$,第一个$相当于转义字符
str.replace(reg, function($, $1, $2) {  // $表示正则表达式匹配的结果, $1第一个子表达式匹配的值
  return $2 + $2 + $1 + $1;
})
string.toUpperCase();  // 变大写
string.toLowerCase();  // 变小写
作业: the-first-name变为小驼峰写法theFirstName
// the-first-name变为小驼峰写法theFirstName
var str = "the-first-name";
var reg = /-(\w)/g;
str.replace(reg, function($, $1) {
  return $1.toUpperCase();  // "theFirstName"
})
作业:字符串去重
// 字符串去重
var str = "aaabbbbdcccdssrrs";
var reg = /(\w)\1*/g;
str.replace(reg, "$1");  // "abdcdsrs"
作业: 数字变成科学计数的样式 1000000 —> 1.000.000
var str = '1000000';
var reg = /(?=(\B)(\d{3})+$)/g;  // 找后面跟三位数字的空格,替换成".",但是第一位不能替换为".",所以空要是非单词边界
str.replace(reg, "."); // "1.000.000"
"100000000".replace(/(?=(\d{3})+$)/g,"."); // ".100.000.000"
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JS渡一教育笔记是一份关于JavaScript编程语言的学习笔记。此笔记主要涵盖了JS的语法和常用功能,旨在帮助初学者从零基础开始学习JS编程。 首先,JS渡一教育笔记包括了JS的基础知识部分,例如数据类型、变量、运算符、条件语句和循环等。这些内容是学习JS编程必不可少的基础,通过学习这些知识,读者可以了解如何声明变量、进行数值计算、编写条件语句等。 其次,JS渡一教育笔记也覆盖了JS中的函数和数组等常用功能。函数是JS中的一种重要的代码封装方式,通过编写函数可以实现代码的复用和模块化,笔记中详细介绍了函数的定义和调用方法。数组是用于存储多个值的数据结构,JS渡一教育笔记也解释了如何创建和操作数组,以及如何使用数组进行数据处理。 此外,JS渡一教育笔记还介绍了JS中的面向对象编程(OOP)。面向对象是一种常用的编程思想,通过将数据和操作封装为对象,可以更好地组织和管理代码。JS渡一教育笔记讲解了面向对象的基本概念和语法,例如如何创建对象、定义类和实现继承等。 最后,JS渡一教育笔记还包含了一些进阶主题,例如DOM操作和AJAX等。DOM是用于操作网页元素的API,JS渡一教育笔记提供了DOM的基本用法和例子。AJAX是一种在不重新加载整个网页的情况下与服务器进行数据交互的技术,笔记中解释了如何使用AJAX发送异步请求和处理响应。 总之,JS渡一教育笔记是一份全面而详细的学习JS编程的资料,可以帮助读者掌握JS的基础知识和常用功能,逐步提升编程能力。无论是初学者还是有一定经验的开发者,都可以从中获得实用的知识和技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值