这一节主要是讲思路
[TOC]
面向对象
对象创建方法
方法一:new Object()
let person1 = new Object();
person1.name = "Joe";
person1.age = 20;
person1.getAge = function () {
console.log(this.age);
}
方法二:字面量化
let person1 = {
name : "Joe" ,
age : 20 ,
getAge : function () {
console.log(this.age);
}
}
方法三:工厂函数封装
// 实质上就是将上面两个方法封装起来
// 封装对象
function Person(name,age) {
let person = new Object();
person.name = name;
person.age = age;
person.showName = function () {
console.log(this.name);
}
return person;
}
let person1 = new Person("Joe",20);
// 封装字面量
function Car(name,color) {
return {
name : name,
color : color,
showName : function () {
console.log(this.name);
}
}
}
let car1 = new Car("BMW","red");
对象内部属性及方法
通俗点说,属性就是用于存放数据的,方法是执行函数
// 属性
person1.name;
// 方法
car1.showName();
原型对象
上述创建对象过程中,每个新对象都会在内部“写”一套共同方法,这些方法会一遍又一遍出现,非常消耗内存资源
为了解决节约内存,我们简单将对象中所有东西分为公共部分和私有部分
公共部分共用一套,私有部分自己一套,这样能有效降低内存消耗
prototype
// 创建私有属性
function Person(name,age) {
this.name = name;
this.age = age;
}
// 创建公共属性、方法
Person.prototype= {
constructor:Person, // 构造函数
version:"1.1", // 对象的静态变量
showName:function () {
console.log(this.name);
}
}
// 注意:公共部分的属性称为 静态成员 ,意思是所有对象共同访问的属性
// 私有部分的属性称为 实例成员 ,意思是只有自身单独访问的属性
// 创建公共属性、方法还可以这么写
// Person.prototype.showAge = function(){};
// 能在之前写的公共部分中添加新的属性、方法
拓展内置对象的公共部分
常用的内置对象有: Array、Function、Math、Object
可以设置一些新的方法来拓展原来内置对象的功能
Array.prototype.getSum = function () {
let sum = 0;
for (let i = 0 ; i < this.length ; i++ )
sum+=this[i];
return sum;
}
console.dir(Array.prototype);
let arr = [1,2,3,4,5,6,7,8,9,10];
console.log(arr.getSum());
// 注意:拓展不能这样写(如下)
Array.prototype = {
getSum:function() {
let sum = 0;
for (let i = 0; i < this.length; i++)
sum += this[i];
return sum;
}
}
/* 这样不会拓展成功
* 原因:这样写会将其他原有方法、属性全部覆盖
* js为了防止内置对象公共部分被污染,会自己拒绝这样拓展方式
*/
继承、函数进阶使用
面向对象特点
封装、继承、多态(抽象)
继承
继承意思为将一个对象内的东西给另一个对象,前者称为父级,后者称为子级
实体对象继承
let parent = {
house: "五大街645号",
shop : "七大街663号",
car : "京AXXXXXXX"
}
let child1 = {};
let child2 = {};
// 方法一:for循环
for (let i in parent)
if (child1[i])
continue;
else
child1[i]=parent[i];
console.log(child1);
// 方法二:函数封装
function extend( parent , child ) {
for (let i in parent)
if (child[i])
continue;
else
child[i]=parent[i];
}
extend(parent,child2);
console.log(child2);
原型对象继承
child.prototype = parent.prototype;
// 记得修改构造函数,不然会使用父级的构造函数
child.prototype.constructor = Child;
call方法
call方法调用时,有两个功能:
- 更改函数内部this的指向(第一个传参)
- 调用函数执行内部代码(之后的传参)
function fun() {
console.log(this.age);
}
let person = {
name:"小明",
age:20
}
fun.call(person);
继承父级对象属性
合理利用call方法将实体化属性写入子级中
function Car( color , carNum ) {
this.color = color;
this.carNum= carNum;
}
function Geely( color , carNum , carSize ) {
Car.call(this,color,carNum);
this.carSize = carSize;
}
let car1 = new Geely("red","京AXXXXXXX","XMC-8");
console.log(car1);
继承父级对象方法
继承时要注意:父级自身的constructor不能继承
// 方法一:for循环一步一步继承
for (let i in Parent.prototype)
/* 若子级自带的方法(包括constructor),不继承 */
if (Child.prototype[i])
continue;
else
Child.prototype[i]=Parent.prototype[i];
// 方法二:原型继承
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
// 注意:原型继承会将Parent的属性写入原型对象中
组合继承
继承父级对象的属性和方法,参考上面
函数进阶使用
注意: 函数fun不加括号为字符串输出,即使赋值也无法执行
函数声明
var fn = function(){};
// 调用必须在声明后面
function fun(){}
// 调用与声明无顺序关系
// 通过创建对象方式声明
var Fun = new Function('a','b','console.log(a+b)');
Fun(24,42);
// 最后一个传参为函数内部程序结构
调用函数和函数中的this
-
普通函数 通过给函数名或者变量名添加 () 方式调用,其中this默认指向window
-
构造函数 通过关键词new进行调用,内部的this指向创建的实例对象
-
对象方法 通过 对象.函数() 方法进行调用,内部的this指向对象自身
-
事件函数 通过触发事件进行调用,内部的this指向事件源
-
定时和延时器事件函数,自动执行,this默认指向window
调用方式 | 非严格模式下 | 备注 |
---|---|---|
普通函数 | window | 严格模式下为undefined |
构造函数 | 实例对象 | 原型方法中this也是实例对象 |
对象方法 | 自身对象 | |
事件绑定 | 事件对象 | |
延时器函数 | window | |
定时器函数 | window |
call方法
call方法调用时,有两个功能:
- 更改函数内部this的指向(第一个传参)
- 调用函数执行内部代码(之后的传参)
fun.call(this,a,b,c ... );
apply方法
apply方法调用时,有两个功能:
- 更改函数内部this的指向(第一个传参)
- 调用函数执行内部代码(之后的传参)
用法与call差不多,但写法与call有区别
var fn = fun.apply(this,[a,b,c ... ]);
bind方法
bind方法调用时,有两个功能:
- 确定函数内部this的指向(第一个传参)
- 绑定函数,这样执行函数时会去访问被绑定函数
var fn = fun.bind(o,a,b,c ... );
函数对象
成员 | 说明 |
---|---|
arguments | 传入实参集合 |
arguments.callee() | 函数本身,arguments的一个属性 |
fn.caller | 函数的调用者,若是全局调用,返回null |
fn.length | 形参个数 |
fn.name | 函数名称 |
高阶函数使用
- 函数作为另一个函数的传参
function fun(fn) {
console.log("这是fun函数");
fn();
}
function fn() {
console.log("我是fn函数");
}
fun(fn);
- 函数作为返回值直接执行
function fun1( num ) {
return function fun2(number) {
console.log(num+number);
}
}
let fun = fun1(100);
fun(20);
函数闭包
函数对其内部作用域内所有部分进行封闭,在函数外无法调用函数内部属性及方法
function outer() {
var a = 10;
}
outer();
console.log(a);
正则表达式
作用: 1. 给定的字符串是否符合正则表达式的过滤逻辑(匹配)
2. 可以通过正则表达式,从字符串中获取我们想要的特定部分(提取)
3. 强大的字符串替换能力(替换)
注意:JavaScript中正则表达式也是个对象
正则表达式语法
创建正则表达式
// 方法一:创建 正则的字面量
var reg1 = /abc/;
// 方法二:通过 正则的构造函数 创建
var reg2 = new RegExp("abc");
字符串与正则表达式
字符串的方法
方法 | 说明 |
---|---|
split() | 根据匹配字符串切割父字符串 |
match() | 使用正则表达式与字符串相比较 返回一个包含匹配结果的数组 |
search() | 对正则表达式或指定字符串进行搜索 返回第一个出现的匹配项的下标 |
replace() | 用正则表达式和字符串直接比较 然后用新的子串来替换被匹配的子串 |
// split方法
let str1 = "aa bbb c dd eeeeee";
console.log(str1.split(" "));
// (8) ["aa", "bbb", "", "", "", "c", "dd", "eeeeee"]
// 使用正则表达式就不会去空字符串
console.log(str.split(/\s+/));
// (5) ["aa", "bbb", "c", "dd", "eeeeee"]
// match方法
let str2 = "abcdefgh";
console.log(str2.match("dee"));
//null
// 没有返回空
console.log(str2.match(/def/));
//["def", index: 3, input: "abcdefghabcdefgh", groups: undefined]
// 有就返回第一个匹配结果的数组
console.log(str2.match(/def/g));// g表示全局变量,意思是搜索全部
// (3) ["def", "def", "def"]
// search方法
let str3 = "abcdefghabcdefgh";
console.log(str3.search("dee"));
//-1
// 没有结果返回-1
console.log(str3.search(/def/));
//3
// 有结果返回第一个匹配的序号
let str4 = "ajklsdajakdslfjmasdsakdadkasmlda";
console.log(str4.replace("a","A"));
//Ajklsdajakdslfjmasdsakdadkasmlda
// 只改变第一个匹配部分
console.log(str4.replace(/a/g,"A"));// 加上全局
//AjklsdAjAkdslfjmAsdsAkdAdkAsmldA
// 改变全部匹配部分
正则表达式的方法
方法 | 说明 |
---|---|
exec() | 在目标字符串中执行一次正则匹配操作 |
test() | 测试当前正则是否能匹配目标字符串 匹配:true;未匹配:false |
let reg = /abc/;
let str = "abcdefgh";
console.log(reg.test(str));
//true
console.log(/ac/.test(str));
//false
str = "abcdefghabcdefghabcdefgh";
console.log(reg.exec(str));
//["abc", index: 0, input: "abcdefghabcdefghabcdefgh", groups: undefined]
console.log(/abc/g.exec(str));// 即使设置全局变量,也只匹配第一个
//["abc", index: 0, input: "abcdefghabcdefghabcdefgh", groups: undefined]
正则表达式的组成
正则表达式分为 普通字符 和 特殊字符(又称元字符)
普通字符:大小写字母、数字
特殊字符:
特殊字符 | 说明 |
---|---|
\t | 制表符 |
\n | 回车符 |
\f | 换页符 |
\b | 空格 |
字符集
简单类:正则的多个字符对应一个字符,可以用 [] 括起来,让 [] 这个整体对应一个字符
o[abc]t ———— oat、obt、oct
范围类:类型又相同时,中间加个横线表示范围:[0-9]、[a-z]、[A-Z]
id[0-9] ————id0、id5
负向类:[]内部最前面加个原四分进行取反,表示匹配内容不为[]内部内容:[^0-9]
o[^0-9]t————oat、o?t、ott
组合类: 允许用中括号匹配不同类型的单个字符:[0-9a-z];
o[0-9a-z]t————o0t、oat、ozt
修饰符
修饰符 | 说明 | 例子 |
---|---|---|
g | 执行全局匹配 | /[0-9]/g |
i | 执行对大小写不敏感的匹配 | /abc/i |
// 之前展示过全局匹配,这里只展示大小写不敏感
console.log(/abc/i.test("ABCD"));
// true
边界
边界 | 说明 | 例子 |
---|---|---|
^ | 开头,不能出现在左中括号后面 | /^123/ |
$ | 结尾,只能写在后面 | /2345$/ |
// ^开头
console.log(/^hello/.test("hello world"));
//true
console.log(/^world/.test("hello world"));
//false
// $结尾
console.log(/hello$/.test("hello world"));
//false
console.log(/world$/.test("hello world"));
///true
// 结合
console.log(/^hello\s+world$/.test("hello world"));
//true
预定义类
预定义字符 | 同义转换 | 说明 |
---|---|---|
. | [^\n\r] | 除换行和回车外的任意字符 |
\d | [0-9] | 数字字符 |
\D | [^0-9] | 非数字字符 |
\s | [ \t\n\x0B\f\r] | 空白字符 |
\S | [^ \t\n\x0B\f\r] | 非空白字符 |
\w | [a-zA-Z_0-9] | 单词字符(字母、数字、下划线) |
\W | [^a-zA-Z_0-9] | 非单词字符 |
量词
量词 | 属性 | 说明 |
---|---|---|
{n} | 硬性量词 | 对应出现零次或者n次 |
{n,m} | 软性量词 | 至少出现n次但不超过m次(中间不能有空格) |
{n,} | 软性量词 | 至少出现n次 |
? | 软性量词 | 出现零次或一次 |
* | 软性量词 | 出现零次及以上 |
+ | 软性量词 | 至少出现一次 |
分组
通过()
与量词组合,表示()
内字符串重复次数
console.log(/(bye){2}/.test("byebye"));
//true
console.log(/(bye){2}/.test("byeBYE"));
//false
或操作符
用|
表示或者关系
console.log(/a|bcd/.test("aaa"));
//true
console.log(/a|bcd/.test("bbcd"));
//true
分组反向引用
第一种用法\N
:匹配\N
第N段字符串作为正则内容进行内部引用
console.log(/^([a-z]{3})\1$/.test("byebye"));
// true
// 引用第一段"bye"字符串作为正则内容匹配之后的内容
console.log(/^([a-z]{3})\1$/.test("byelie"));
//false
第二种用法$N
:匹配\N
第N段字符串作为正则内容进行外部引用
var str = "123*456".replace(/^(\d{3})\*(\d{3})$/,"$2*$1");
console.log(str);
//456*123
匹配中文字符
/^[a-z\u4e00-\u9fa5]+$/
ES6(ES2015)语法规则
ES5为09年规范的
ES2015及其之后的更新的版本都俗称为 ES6
作用域
在ES5时,作用域有两种:全局作用域、函数作用域
ES2015之后,作用域分三类:全局作用域、函数作用域、块级作用域
新增的 块级作用域
// 简单来说,块就是 {}
if (true) {
}
声明方式
ES5:var
缺点:在window对象内声明的,无论在那里声明都是全局变量
ES6:const 、 let
let: 区域性声明,只能在作用域内使用,作用域外无法访问
const: 常量/恒量,声明时必须赋值,赋值后无法修改,相当于只读状态下的let
建议:不用var,主用const,let协作
解构
数组的解构
数组解构的意思相当于获取数组某个位置上面的值
const arr = [0,1,2,3,4,5,6,7];
// 常规方法
const num1 = arr[0];
const num2 = arr[1];
// 解构方法
const [n1,,]=arr;
// 若是取第几个,则在前面标注几个逗号,后面的逗号可以省略
console.log(n1);
//0
const [,,,n2]=arr;
console.log(n2);
//3
const [,,n3,n4,n5]=arr;
// 可以一次声明多个常量/变量
// 解构中的 ...
const [,N] = arr;
// ... 会自动从当前位置匹配到末端位置
console.log(N);
//(7) [1, 2, 3, 4, 5, 6, 7]
对象解构
对象解构的意思相当于获取对象某个成员上面的值
const obj = {
const obj = {
name:"张三",
age:20,
place:"云南"
};
// 常规方法
const name = obj.name;
const age = obj.age;
// 解构方法
const {name:name1,age:age1,place} = obj;
// 写法有两种:
// 第一种是 属性名:变量/常量
// 第二种是 属性名与常量/变量名称一致
// 但是一般建议使用第一种
console.log(name1,age1,place);
//张三 20 云南
模板字面量
模板字面量与字符串
可以用模板字面量来代替字符串表达
const str = `this
is a \`string`
console.log(str)
//this
// is a `string
// 也可以将其他变量/常量直接写入,甚至可以运算以及方法函数调用
const name = "张三";
console.log(`${name}今年${10+12}岁,幸运数字为${parseInt(Math.random()*(0+300))}`);
//张三今年22岁,幸运数字为272
模板字面量与函数调用
可以用模板字面量来执行函数
const str = console.log`hello JavaScript`;
//["hello JavaScript", raw: Array(1)]
// 将模板内容作为执行时输入参数
function fun( string , name , age ) {
return string[0] + name + string[1] + age + string[2] + (age>18? "It's an adult.":"It's a child");
}
let name = "Joe";
let age = 8;
let nnn = fun`hello , ${name} is a ${age} years old boy.`;
console.log(nnn)
//hello , Joe is a 8 years old boy.It's a child
模板字面量与方法
方法 | 说明 |
---|---|
.startsWith( str ) | 是否以 str 字符/字符串 开头 返回值为 布尔型 |
.endsWith( str ) | 是否以 str 字符/字符串 结尾 返回值为 布尔型 |
.includes( str ) | 是否存在 str 字符/字符串 返回值为 布尔型 |
函数新特性
参数默认值
function fun( string = "hello world" ) {
console.log(string);
}
fun("1223");
//1223
fun();
//hello world
剩余参数
function fun(n,...args) {
console.log(args)
}
fun(1,2,3,4)
//(3) [2, 3, 4]
箭头函数
const fun = (a,b,...arr) =>{
console.log(a,b,arr);
}
fun(1,2,3,4,5,6,7);
//1 2 (5) [3, 4, 5, 6, 7]
// filter方法
const arr = [1,2,3,4,5,6,7,8];
console.log(arr.filter(i=>i%3));
//(6) [1, 2, 4, 5, 7, 8]
箭头函数与this
// 箭头函数没有prototype,函数自身没有this
// 定义this指向时,会继承自外层的第一个普通函数的this
const person = {
name: "tom",
sayHi: function () {
setTimeout(() => {
// this指向对象本身
console.log(`hi,my name is ${this.name}`)
},1000);
}
}
person.sayHi()
//hi,my name is tom
对象新特性
字面量增强
const bar = "bar"
const age = "age"
const obj = {
name: "tom",
// 可以直接使用外部变量进行赋值,且变量名会变成对象的属性名
bar,
sayHi () {
console.log('hi')
console.log(this)
},
// 计算属性名
[1+2]: 18
}
obj[age] = 18;
console.log(obj.bar);
//bar
obj.sayHi();
//hi
//{3: 18, name: "tom", bar: "bar", age: 18, sayHi: ƒ}
console.log(obj[3]);
//18
扩展方法
// Object.assign 方法
// 作用:将一个或者多个对象组合分配到第一个对象中
// Object.assign( target , ...source )
const source1 = {
a: 123,
b: 123
}
const source2 = {
b: 678,
d: 789
}
const target = {
a:456,
c:789
}
const result = Object.assign(target,source1,source2)
console.log(target)
//{a: 123, c: 789, b: 678, d: 789}
/* 组合原则:
* 1.第一个对象中的属性/函数在组合中未能出现,该属性/方法不会变
* 2.若在后续对象中出现相同属性/方法,靠后的会覆盖之前的
* 3.返回值为第一个对象
*/
console.log(target === result)
//true
// 拓展思路
// 1.复制对象
const obj = target.assign({},target);
// 2.简化参数(构造函数为例)
function child( parent , others ) {
this.assgin(this , parent , others);
}
判断
// Object.is 方法
console.log(NaN===NaN);
//false
console.log(Object.is(NaN,NaN));
//true
console.log(+0===-0);
//true
console.log(Object.is(+0,-0));
//false
class创建对象
传统构造对象时要创建要先写构造函数,再写原型对象
可以通过class 一次性创建到位
class Person {
// 构造函数
constructor (name, age) {
this.name = name;
this.age = age;
}
// 静态成员
sayHi () {
console.log(`hi,my name is ${this.name}`);
}
}
let p1 = new Person("Joe",20);
console.log(p1);
静态方法
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
// 只能在class内使用
static create (name,age) {
console.log(this)
return new Person(name,age)
}
}
类的继承
ES6中加入关键词 extends ,方便继承
继承的父类会储存在子类静态属性中的静态属性内
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
}
class Student extends Person {
constructor (name,age,number) {
super(name,age)
this.number = number
}
hello () {
super.sayHi()
console.log(`学号是 ${this.number}`)
}
}
let s1 = new Student("Joe",20,13333);
console.log(s1);
内置对象
Set对象
属性/方法 | 说明 |
---|---|
size | 属性,返回Set中值的个数 |
add( value ) | 方法,在Set末尾添加一个新元素 |
clear() | 方法,清空Set内所有元素 |
delete( value ) | 方法,移除Set内值相同的元素,返回布尔型 true表示元素存在且被移除,false表示不存在 |
forEach( callbackFn[, thisArg] ) | 方法,Set对象中的每一个值调用一次callBackFn(方法) |
has( value ) | 方法,判断Set对象中是否有该值 |
values() | 方法,返回一个新的Set对象,包含Set对象中所有元素 |
keys() | 方法,效果同上 |
let mySet = new Set();
mySet.add(1); // Set [ 1 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add("some text"); // Set [ 1, 5, "some text" ]
let o = {a: 1, b: 2};
mySet.add(o);
mySet.add({a: 1, b: 2}); // o 指向的是不同的对象,所以没问题
mySet.has(1); // true
mySet.has(3); // false
mySet.has(5); // true
mySet.has(Math.sqrt(25)); // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true
mySet.size; // 5
mySet.delete(5); // true, 从set中移除5
mySet.has(5); // false, 5已经被移除
mySet.size; // 4, 刚刚移除一个值
console.log(mySet);
// logs Set(4) [ 1, "some text", {…}, {…} ] in Firefox
// logs Set(4) { 1, "some text", {…}, {…} } in Chrome
Map对象
属性/方法 | 说明 |
---|---|
size | 属性,返回对象键值对的数量 |
set( key, value ) | 方法,设置对象中的键和键的值 其中键可以是函数,对象等 |
get( key ) | 方法,查询特指的键的值,没有返回undefined |
has( key ) | 方法,返回布尔值,表示Map中是否含有对应的值 |
delete( key ) | 方法,移除对应的键值对,返回true;若不存在返回false |
clear() | 方法,移除Mao内所有键值对 |
forEach( callbackFn[,thisArg] ) | 方法,按插入顺序,为 Map 对象里的每一键值对调用一次callbackFn函数 |
values() | 方法,返回一个Iterator对象,按顺序插入Map对象的每个元素的值 |
let myMap = new Map();
let keyObj = {};
let keyFunc = function() {};
let keyString = 'a string';
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
myMap.get('a string'); // "和键'a string'关联的值"
// 因为keyString === 'a string'
myMap.get({}); // undefined, 因为keyObj !== {}
myMap.get(function() {}); // undefined, 因为keyFunc !== function () {}
新的数据类型
Symbol
作用:为对象添加一个独一无二的属性标识符
// 注意:Symbol没有构造函数,不能用new方法
let test = Symbol("123");
console.log(typeof test);
//symbol
console.log(Symbol("fff")==Symbol("fff"));
//false
// 使用for() 方法可以全局共享Symbol
console.log(Symbol.for(true)==Symbol.for("true"));
//true
// toStringTag
循环的新方法
for of
作用:访问(或者说是遍历)对象内部的值
const arr = [100,200,300,400,500];
for (const item of arr) {
console.log(item);
}
//100
//200
//300
//400
ES2016
const arr = [1,true,NaN,23,'hello']
console.log(arr.indexOf(true))
//1
console.log(arr.indexOf(null))
//-1
console.log(arr.indexOf(NaN))
//-1
// NaN无法被识别
// includes 包括
// 作用:数值内是否有这个值,返回布尔型
console.log(arr.includes(NaN));
// 指数运算符 **
console.log(2**10);