day1

原型对象的意义

共享数据,节省内存

往原型对象添加属性或方法

function People(name,age){

	this.name=name;//需要单独出来的属性

	this.age=age;

}//类似 java中的类

People.prototype.eyeNumber=2;//不需要单独出来的属性,所有People对象的共有属性

People.prototype.eat=function(){//类似java中的静态方法

	console.log("吃饭了");

}

People.prototype.study=function(){

	this.eat();//通过this对象调用该对象的方法

	console.log("吃饱了,学习了");

}

//==========================华丽的分割线=========================================
//往原型对象添加属性方法更简单的方式
Person.prototype = {
  constructor: Person, // => 手动将 constructor 指向正确的构造函数
  type: 'human',
  sayHello: function () {
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

使用建议

原型对象使用建议

  • 私有成员(一般就是非函数成员)放到构造函数中
  • 共享成员(一般就是函数)放到原型对象中
  • 如果重置了 prototype 记得修正 constructor 的指向

构造函数、实例、原型三者之间的关系

任何函数都具有一个 prototype 属性,该属性是一个对象。

function F () {}
console.log(F.prototype) // => object

F.prototype.sayHi = function () {
  console.log('hi!')
}

构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数。

console.log(F.constructor === F) // => true

通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 __proto__

var instance = new F()
console.log(instance.__proto__ === F.prototype) // => true

`__proto__` 是非标准属性。

实例对象可以直接访问原型对象成员。

instance.sayHi() // => hi!

总结:

  • 任何函数都具有一个 prototype 属性,该属性是一个对象
  • 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数
  • 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 __proto__
  • 所有实例都直接或间接继承了原型对象的成员

day2

贪吃蛇案例

day3

继承

<!--严格模式-->
"use strict";
function Person(name,age,sex){//被继承的构造函数
    this.name=name;
    this.age=age;
    this.sex=sex;
}
Person.prototype.eat=function () {//被继承对象原型里面的方法
    console.log("吃饭了");
}
function Student(name,age,sex,score){//继承了Peoson
    Person.call(this,name,age,sex);//借用Person的对象的构造器,实现非原型属性行为的继承
    this.score=score;//继承以外的自身属性
}
Student.prototype=new Person();//原型的继承
Student.prototype.study=function () {//往继承了之后的原型对象里面添加自己的行为
    console.log("开始学习了兄弟");
}
var std=new Student("张三",20,"男",100);//实现继承了,可以直接调用
console.log(std.name,std.age,std.sex,std.score);
std.eat();
std.study();

严格模式

“use strict”;

在JavaScript上添加该语句,即使用了严格模式,严格模式是指语法严格

函数进阶

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

函数也是对象

  • 所有函数都是 Function 的实例

day4

##apply和call方法

方法介绍

两个方法都是Object中的prototype中的方法,因为所有对象都是继承自Object所以这两个方法所有对象都可以使用

作用介绍

两个方法都可以调用方法,只是在于形式的不一样

function f1(x,y){

​console.log(x+y);

}

//第一种执行方法

f1(1,2);

//第二种执行方法,第一个参数是调用者的对象,默认为window。第二个参数是数组,数组内存放函数参数

f1.apply(null,[1,2]);

//第三种执行方法,第一个参数是调用者的对象,默认为window。后面多个参数都是函数的参数

f1.call(null,1,2);

//演示案例

function Person(){

	this.sayHi=function(){

		console.log(this.hello);

	}

}

function Student(){

	this hello="HelloWorld";

	this.sayHi=function(){

		new  Person().sayHi.apply(this);

	}

}

var stu=new Student();

stu.sayHi();

//输出结果是HelloWorld

//Person里没有hello这个变量,如果由Person的对象来调用就会报错,但是在Student里面通过apply这个方法来调用,把Student对象来执行这个方法,就能顺利执行

bind方法

作用介绍:

复制函数,绑定调用他的对象

即可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数

语法:var ff=fn.bind(obj);ff();

参数传递

支持在绑定的时候传递多个方法参数,也支持复制后传参

异同

和apple和call相比,可以绑定但不执行

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

##函数的其他成员

  • arguments
    • 实参集合
  • caller
    • 函数的调用者
  • length
    • 形参的个数
  • name
    • 函数的名称

函数闭包

什么是闭包

闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途:

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中

一些关于闭包的例子

示例1:

var arr = [10, 20, 30]
for(var i = 0; i < arr.length; i++) {
  arr[i] = function () {
    console.log(i)
  }
}

示例2:

console.log(111)

for(var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}
console.log(222)

递归

自己调用自己

案例–深度拷贝对象

for (var item in a) {
    if (a instanceof Array) {
        b[item] = [];
        copyEle(a[item], b[item]);
    } else if (a instanceof Object) {
        b[item] = {};
        copyEle(a[item], b[item]);
    } else {
        //    普通属性
        b[item] = a[item];
    }
}

案例–遍历DOM树

(function () {
    function showDom(root, spaceNum) {
        var spaceStr = "";
        for (var i = 0; i < spaceNum; i++) {
            spaceStr += "-";
        }
        console.log(spaceStr + root.nodeName);
        if (!root.childNodes) {
            return;
        } else {
            // console.log(root.children.length)
            for (var i = 0; i < root.children.length; i++) {
                showDom(root.children[i],spaceNum+2);
            }
        }
    }

    window.showDom = showDom;
}());
// console.log(document.documentElement.nodeName);
showDom(document.documentElement, 0);

高级函数–函数作为返回值案例

比较排序案例

(function () {
    var movies = [
        {"海报": "images/kbjs.jpg", "影片名": "狂暴巨兽", "上映时间": "2018-04-13", "类型": "动作  科幻  冒险 "},
        {"海报": "images/wwxd.jpg", "影片名": "无问西东", "上映时间": "2018-01-12", "类型": "爱情  战争  剧情 "},
        {"海报": "images/wbsys.jpg", "影片名": "我不是药神", "上映时间": "2018-07-05", "类型": "喜剧  剧情 "},
        {"海报": "images/thwj.jpg", "影片名": "头号玩家", "上映时间": "2018-03-29", "类型": "动作  科幻  冒险 "}
    ];
    //根据需求获取比较器
    var getCompare=function(attr){
        return function (obj1,obj2) {
            return obj1[attr]>obj2[attr]?-1:1;
        }
    }
    // movies.sort(getCompare("海报"));
    // movies.sort(getCompare("影片名"));
    movies.sort(getCompare("上映时间"));
    // movies.sort(getCompare("类型"));

    //把数组内的元素部署到表格内,在界面显示出来
    var init=function () {
        var tbody=document.querySelector("tbody");
        for (var i = 0; i < movies.length; i++) {
            //行标签
            var tr=document.createElement("tr");
            //海报标签
            var imgTd=document.createElement("td");
            var img=document.createElement("img");
            img.src=movies[i]["海报"];
            imgTd.appendChild(img);
            //影片名标签
            var movieName=document.createElement("td");
            movieName.innerText=movies[i]["影片名"];
            //上映时间标签
            var releaseDate=document.createElement("td");
            releaseDate.innerText=movies[i]["上映时间"];
            //类型标签
            var type=document.createElement("td");
            type.innerText=movies[i]["类型"];
            //    把标签全部添加当前tr标签
            tr.appendChild(imgTd);
            tr.appendChild(movieName);
            tr.appendChild(releaseDate);
            tr.appendChild(type);
            //把行标签添加到tbody标签中
            tbody.appendChild(tr);
        }
    }
    init();
}());

day6

正则表达式

注意点:在正则表达式中,在非严谨模式下,正则对象的test方法测试的字符串只要有部分字符串符合正则的规则即返回true

###正则表达式的作用

  1. 给定的字符串是否符合正则表达式的过滤逻辑(匹配)

  2. 可以通过正则表达式从字符串中获取特定部分(提取)

正则表达式的组成

  • 普通字符
  • 特殊字符(元字符):正则表达式中有特殊意义的字符

常用元字符串

限定符

其它

[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
[^]  匹配除中括号以内的内容
\ 转义符
| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
() 从两个直接量中选择一个,分组
   eg:gr(a|e)y匹配gray和grey
[\u4e00-\u9fa5]  匹配汉字

参数

JavaScript中使用正则表达式

创建正则对象有两种方式

方式一:var reg=new Regex(’\d’,‘i’);

方式二:var reg=/\d/i;

常用正则表达式

// 要求5~15位,不能以0开头,只能是数字*/
qq: /^[1-9][0-9]{4,14}$/,
phone: /^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$/,
email: /^[0-9a-zA-Z_]+[@][0-9a-zA-Z_]+([.][0-9a-zA-Z_]+){1,2}$/,
/* 中国的座机号码是有三个部分,分别是代表当地的区号,分隔符及具体的号码。即:000-00000000;
    区号:以数字0开始,并跟随2-3个数字‘
    分隔符:以’-‘代替,便于书面的理解;
    具体的号码:大部分地区的号码是7-8位数字组成;*/
telephone: /^0\d{2,3}-\d{7,8}$/,
//2到6位中文
fullName: /^[\u4e00-\u9fa5]{2,6}$/

字符串提取

  • 分组提取

    特点:用正则把字符串分组,然后用RegExp对象的$[组号]来提取

var str="2018-07-28";
var reg1=/^([\d]{4})[-]([\d]{1,2})[-]([\d]{1,2})$/;
if (reg1.test(str)){
    console.log(RegExp.$1);
    console.log(RegExp.$2);
    console.log(RegExp.$3);
}
  • 字符串match(reg)方法提取

    特点:把目的字符串的正则表达式去匹配源字符串,返回一个数组(匹配的方式可以是全局g和忽略大小i或全局忽略大小gi)

    var str="2018-07-28";
    var reg2=/[\d]{2,4}/g;
    var arr=str.match(reg2);
    //arr是一个数组
    console.log(arr);
    
  • reg的exec方法

    特点:执行一次匹配一个,匹配完毕后会再匹配null,在匹配是从头开始

var str="2018-07-28";
var reg2=/[\d]{2,4}/g;
var content;
while(content=reg2.exec(str)){
    console.log(content);
}

补充

伪数组和数组

在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function)。

什么是伪数组

  1. 拥有 length 属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
  2. 不具有数组所具有的方法

伪数组,就是像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组,比如:

var fakeArray = {
  "0": "first",
  "1": "second",
  "2": "third",
  length: 3
};

for (var i = 0; i < fakeArray.length; i++) {
  console.log(fakeArray[i]);
}

Array.prototype.join.call(fakeArray,'+');

常见的伪数组有:

  • 函数内部的 arguments
  • DOM 对象列表(比如通过 document.getElementsByTags 得到的列表)
  • jQuery 对象(比如 $("div")

伪数组是一个 Object,而真实的数组是一个 Array。

伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多方法,比如:

var arr = Array.prototype.slice.call(arguments);

Array.prototype.forEach.call(arguments, function(v) {
  // 循环arguments对象
});

// push
// some
// every
// filter
// map
// ...

以上在借用数组的原型方法的时候都可以通过数组直接量来简化使用:

var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
}

;[].push.call(obj, 'd')

console.log([].slice.call(obj))

;[].forEach.call(obj, function (num, index) {
  console.log(num)
})

小结

  • 对象没有数组 Array.prototype 的属性值,类型是 Object ,而数组类型是 Array
  • 数组是基于索引的实现, length 会自动更新,而对象是键值对
  • 使用对象可以创建伪数组,伪数组可以正常使用数组的大部分方法

附录

A 代码规范

代码风格

校验工具

B Chrome 开发者工具

C 文档相关工具

10-05 14:32