此笔记是我在拉勾学习课程过程中的总结,文字原创,笔记里很多技巧和知识是老师总结的,如果有侵权,请联系本人!
从面向过程到面向对象的确是一个挺难的转变,但是面向对象的好处的确显而易见,更紧凑的结构,更简洁的代码,封装性也更好,不用操心对象的实现过程,传参数,得结果,调方法,都比较方便。
一、面向对象
1.什么是对象
我们可以从两次层次来理解:
(1) 对象是单个事物的抽象。
(2) 对象是一个容器,封装了属性(property)和方法(method)。
属性:对象的状态
方法:对象的行为
区分对象和类:对象是单个事物的抽象,而不是一类事物的抽象
2.面向对象编程
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。
对比面向过程和面向对象:
• 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟
• 面向对象就是找一个对象,指挥得结果
• 面向对象将执行者转变成指挥者
• 面向对象不是面向过程的替代,而是面向过程的封装
<script>
// 面向过程
// 定义学生对象
// var std1 = {name:"bob",score: 89};
// var std2 = {name:"mike",score: 98};
// // 疯转给一个打印学生成绩的函数
// function printScore (student) {
// console.log("姓名:" + student.name + "成绩:" + student.score);
// }
// // 调用函数打印具体成绩
// printScore(std1);
// printScore(std2);
// 面向对象
// 首先,考虑的额不是整个流程,二是将一个学生当成一个对象,对象有两个属性存储姓名和成绩,并且对象自己
// 有打印成绩功能,将所有的有关属性和行为都封装到对象身上
// 在这个过程中,我们一直会有多个类似的对象,可以利用构造函数的方法先封装,然后创建单独对象
// 1.抽象数据行为成为一个模板(class)
function Student (name,score) {
this.name = name;
this.score = score;
this.printScore = function () {
console.log("姓名:" + this.name + "成绩:" + this.score);
};
}
// 根据模板创建具体的实例对象(instance)
var std1 = new Student("bob",90);
var std2 = new Student("mike",98);
// 实例调用对象自己的方法
std1.printScore();
std2.printScore();
</script>
3.面向对象特性
封装、继承、多态(抽象)
4.创建对象的几种方式
• new Object() 构造函数
• 对象字面量 {}
• 工厂函数
• 自定义构造函数
构造函数和实例对象的关系:
构造函数是根据具体的事物抽象出来的抽象模板
每个实例对象含有一个constructor属性,并指向构造函数
但是要用 instanceof 判断一个对象的具体对象类型。
// 通过构造函数生成的对象都有一个constructor属性 ,构造器,构造函数
// 每个对象的constructor 属性值就是生成的这对象的构造函数
// 可以通过它找到 对象的出处,是原生的还是自定义的
// console.log(arr.constructor);
// console.log(person1.constructor);
// 判断一个对象的具体对象类型,需要使用instanceof 进行判断,constructor方法不严谨
console.log(person1 instanceof Person);
console.log(person1 instanceof Array);
</script>
5.实例成员和静态成员(只有构造函数生成对象的时候区分)
实例成员:在构造函数内部添加给this的成员,创建实例对象后必须有对象调用。
静态成员:添加给构造函数自身的成员,只能使用构造函数调用。
<script>
function Person (name,age) {
// 实例成员,通过将来生成的实例对象进行调用的成员
实例成员,直接添加给函数内部的 this
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
// return instance;
}
// 静态成员-- 直接给构造函数添加的成员
Person.verson = '1.0';
// 生成实例
var person1 = new Person('bob',18);
var person2 = new Person('mike',20);
// 调用实例成员
console.log(person1.name);
// 使用构造函数调用实例成员会出错
// console.log(Person.name);//这里会输出一个person ,这是构造函数本身的名字
// Person.sayName();
// 调用静态成员,只能通过构造函数进行调用
console.log(Person.verson);
console.log(person1.verson);
</script>
6.构造函数与原型
构造函数
- 首字母要大写
- 使用new关键字创建对象
- 浪费内存
原型特点:
• 任何函数都具有一个 prototype 属性,因为有了该属性,我们可以使用构造函数的prototype给原型对象添加属性和方法。
•proto和构造函数的原型对象prototype里面都有一个constructor属性-- 指向构造函数本身。
• 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针proto,所以在使用属性和方法时,实例对象可以向原型对象查找属性和方法
• 实例对象可以直接访问原型对象成员。
// 定义构造函数
function Person (name,age) {
this.name = name;
this.age = age;
}
// 获取构造函数的 prototype 属性
console.log(Person.prototype);
// prototype属性值是一个对象,通常叫做原型对象
// 对象属性内部可以添加一些属性和方法
Person.prototype.type = "human";
Person.prototype.sayHi = function () {
console.log('hello');
};
// 构造函数的 原型对象上面都有一个默认的 constructor 属性
// console.log(Person.prototype.constructor);
// 创建实例对象
var p1 = new Person('mike',18);
// 所有的对象都有一个 __proto__ 的属性,是一个指针,指向的就是生成实例对象的 构造函数的原型对象
console.log(p1.__proto__);
console.log(p1.__proto__ === Person.constructor);
// __proto__属性并不是标准属性,是浏览器自己根据语法自动生成的
// p1.__proto__.sayHi();
p1.sayHi();
// 在真正的开发过程中,不会用__proto__属性
- 我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。
- 因为每个构造函数都有一个prototype属性,并指向原型对象,同时其属性和方法被实例所拥有
- 解决内存浪费问题
<script>
// var fns = {
// sayName : function () {
// console.log(this.name);
// },
// sayAge : function () {
// console.log(this.age);
// }
// };
function Person (name,age) {
this.name = name;
this.age = age;
}
// 更优化的解决方法,将所有的实例共享的属性和方法,都添加给原型对象,添加公用的属性和方法
Person.prototype.type = "human";
Person.prototype.sayName = function () {
// 方法调用时,哪个对象调用,this就指向谁
console.log(this.name);
};
Person.prototype.sayAge = function () {
console.log(this.age);
};
// 生成实例对象
var person1 = new Person('bob',18);
var person2 = new Person('mike',20);
// 调用原型对象上的公用的属性和方法
person1.sayAge();
console.log(person1.sayName === person2.sayName);
</script>
7.原型链
原型链查找机制:
- 搜索首先从对象实例本身开始
- 如果在实例中找到了具有给定名字的属性,则返回该属性的值
- 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性
- 如果在原型对象中找到了这个属性,则返回该属性的值
<script>
function Person (name,age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log('hello');
};
}
// 更优化的解决方法,将所有的实例共享的属性和方法,都添加给原型对象,添加公用的属性和方法
Person.prototype.type = "human";
Person.prototype.sayName = function () {
// 方法调用时,哪个对象调用,this就指向谁
console.log(this.name);
};
Person.prototype.sayAge = function () {
console.log(this.age);
};
// 生成实例对象
var person1 = new Person('bob',18);
var person2 = new Person('mike',20);
// var o = person1.__proto__;//指向的是person构造函数的原型对象
// // 任何一个对象都有__proto__属性,指向的就是该对象的 构造函数的 原型对象
// var o2 = o.__proto__;
// console.log(o2.constructor); //指向object
// console.dir(o2);
// console.dir(o2.__proto__); //null
实例对象读写原型对象成员
读取:
先在自己身上查找,找到即回
自身找不到,沿原型链找,找到返回
一直到末端找不到,返回undefined
实例对象读写原型对象成员
值类型成员写入(实例对象.值类型成员 = xx):
- 当实例期望重写原型对象中的某个普通数据成员时实际上会把该成员添加到自己身上
- 也就是说该行为实际上会屏蔽掉对原型对象成员的访问
引用类型成员写入(实例对象.引用类型成员 = xx): - 同上
复杂类型成员修改(实例对象.成员.xx = xx): - 同样会先在自己身上找该成员,如果自己身上找到则直接修改
- 如果自己身上找不到,则沿着原型链继续查找,如果找到则修改
- 如果一直到原型链的末端还没有找到该成员,则报错(实例对象.undefined.xx = xx)
<script>
function Person (name,age) {
this.name = name;
this.age = age;
// this.sayName = function () {
// console.log('hello');
// };
}
// 更优化的解决方法,将所有的实例共享的属性和方法,都添加给原型对象,添加公用的属性和方法
Person.prototype.type = "human";
Person.prototype.sayName = function () {
// 方法调用时,哪个对象调用,this就指向谁
console.log(this.name);
};
Person.prototype.sayAge = function () {
console.log(this.age);
};
// 添加新的属性给原型对象,值是一个对象类型
Person.prototype.addresss = {
city : '北京'
};
// 生成实例对象
var person1 = new Person('bob',18);
var person2 = new Person('mike',20);
// 读取 属性和方法
// console.log(person1.type);
// console.log(person1.city);//undefined
console.log(person2.sayAge);
// 通过实例对象添加的新成员,会直接添加给自己,会屏蔽掉对原型对象的访问
person1.sex = "male";
person1.sayAge = function () {
console.log(this.age);
};
// 如果通过实例对象更改原型对象的属性和方法,会直接添加给自己,会屏蔽掉对原型对象的访问
person1.type = 'person';
person1.sayName = function () {
console.log(this.name);
};
console.dir(person1);
// 通过实例对象更改原型对象中复杂类型数据中的内容,还是会进行原型链查找
person1.addresss.city = '上海';
</script>
更简单的原型语法
为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,将 Person.prototype 重置到一个新的对象。
注意:原型对象会丢失 constructor 成员,所以需要手动将 constructor 指向正确的构造函数。
建议:
在定义构造函数时,可以根据成员的功能不同,分别进行设置:
- 私有成员(一般就是非函数成员)放到构造函数中
- 共享成员(一般就是函数)放到原型对象中
- 如果重置了 prototype 记得修正 constructor 的指向
function Person (name,age) {
this.name = name;
this.age = age;
}
// 更优化的解决方法,将所有的实例共享的属性和方法,都添加给原型对象,添加公用的属性和方法
// Person.prototype.type = "human";
// Person.prototype.sayName = function () {
// // 方法调用时,哪个对象调用,this就指向谁
// console.log(this.name);
// };
// Person.prototype.sayAge = function () {
// console.log(this.age);
// };
// 直接使用一个对象字面量 对原型对象赋值
Person.prototype = {
construtor : Person, // 需要手动 将constructor 属性指向正确的构造函数
type : 'human',
sayName : function () {
console.log(this.name);
}
};
// 生成实例对象
var person1 = new Person('bob',18);
var person2 = new Person('mike',20);
8.JS原生构造函数的原型对象
所有函数都有 prototype 属性对象。
JavaScript中的内置构造函数也有 prototype 原型对象属性:
- Object.prototype
- Function.prototype
- Array.prototype
- String.prototype
- Number.prototype
9.ES6中关于类的新特性
9.1class类(回到java那边了,哈哈哈)
<script>
// class 类
// function Person(name, age) {
// this.name = name;
// this.age = age;
// }
// Person.prototype.sayHi = function () {
// console.log(hi,my name is ${this.name}
)
// }
class Person {
constructor (name,age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
}
const p1 = new Person("zs",18)
console.log(p1);
p1.sayHi()
</script>
9.2static 静态方法
<script>
// 静态方法
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
static create (name,age) {
console.log(this);//指向Person,而不是p1
return new Person(name,age)
}
}
const p1 = Person.create("tom",18)
console.log(p1);
</script>
9.3类的继承
call方法:改变this指向,调用函数
<script>
// call
// 函数本身就是一种对象,能够有自己的属性和方法
// call方法本身是一种执行函数的方法
function fn (a,b) {
console.log(this);
console.log(a+b);
}
var o = {
name : "zs"
}
// 普通函数调用
fn(2,2);
// call函数调用函数的时候,有两个功能
// 1.更改函数内部this指向
// 2.调用函数执行内部代码
// 参数:第一个制定this,后面传入实参
fn.call(o,3,4);
</script>
9.3.1对象拷贝:for...in
<script>
// 父级对象
var laoli = {
name: "laoli",
money: 100000,
house: ["上铺","住宅"],
tech: function () {
console.log("厨艺");
}
};
// 子集的对象
var xiaoli = {
name : "xiaoli"
}
// 对象之间的继承,使用 for....in
// for (k in laoli) {
// if (xiaoli[k]) {
// continue;
// }
// xiaoli[k] = laoli[k];
// }
// 封装一个对象之间继承的函数、
function extend (parent,child) {
// 对象之间的继承,使用 for....in
for (k in parent) {
if (child[k]) {
continue;
}
child[k] = parent[k];
}
}
extend(laoli,xiaoli);
console.log(xiaoli);
</script>
9.3.2借用构造函数继承构造函数的属性
<script>
// 人类类型
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 学生类型
function Student(name,age,sex,score) {
// 直接对父类型的构造函数进行一个普通调用
// Person 普通调用过程中,内部的 this 指向 window
// 可以通过 call 方法更改Person 内部的 this
Person.call(this,name,age,sex);
this.score = score;
}
// 老师类型
function Teacher(name,age,sex,salary) {
Person.call(this,name,age,sex);
this.salary = salary;
}
// 创建一个学生的实例对象
var s1 = new Student("zs",18,"male",89);
var s2 = new Student("ls",18,"female",95);
console.dir(s1);
console.dir(s2);
</script>
9.3.3构造函数原型方法继承
组合继承
<script>
// 组合继承:属性在构造函数内部继承,方法通过原型继承
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function () {
console.log("hello");
};
function Teacher (name,age,salary) {
// 继承父类属性
this.salary = salary;
Person.call(this,name,age);
}
// 方法继承,通过原型对象继承
Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;
var t1 = new Teacher("wang",30,10000);
t1.sayHello();
</script>
9.3.4ES6新增类的继承
<script>
//继承
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(`hi,my number is ${this.number}`)
}
}
const s1 = new Student("tom",18,101)
s1.hello()
</script>
二、函数进阶
1.函数的定义方式
函数声明的方式 命名函数
函数表达式 匿名函数
new Function 参数是字符串的格式
所有函数都是Function的实例 函数也是对象
很少会用到这种定义方式
// var f = new Function('console.log(123)');
var f = new Function('a','b','console.log(a+b)');
f(1,2);
2.函数的调用和this指向
<script>
// 1.普通函数,通过给函数名或者变量名添加()方式执行
// 内部的this指向window
function fun() {
console.log(1);
}
fun();
// 2.构造函数,通过new 调用
// 内部 this 指向将来创建的实例对象
function Person (name) {
this.name = name;
}
var p1 = new Person("zs");
// 3.通过对象的方法,是通过对象打点调用函数,然后加小括号
// 内部的 this 默认指向调用的对象自己
var o = {
sayHi: function () {
console.log("haha");
}
}
o.sayHi();
// 4.事件函数,不需要特殊符号,只要事件被触发,就会自动执行
// 事件函数内部的 this 指向的是事件源
document.onclick = function () {
console.log("事件");
};
// 5.定时器和延时器函数,不需要加特殊符号,只要执行后,在对顶事件自动执行
// 默认内部的 this 指向的是 window
setInterval(function() {
console.log("hi");
},2000);
</script>
// this的指向是要联系执行的上下文,在调用的时候,
是按照什么方式调用,指向是不一样的
- 原型对象的this ---> 指向创建出来的实例
- 绑定事件的函数 触发相应事件调用 this ---> 指向触发事件的对象
- 立即函数 自动调用 (function () {})() this ---> window
3.改变函数内部this指向的方法
Call、apply和bind
call() 方法:
fun.call(this,arg1, arg2, arg3, ...)
如果this指定了 null 或者 undefined 则内部 this 指向 window
apply() 方法:
fun.apply(this, [argsArray])
bind() 函数:
fun.bind(this,arg1, arg2, arg3, ...)
• this:当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
·arg1, arg2, arg3这些参数可以绑定的时候传一部分,调用的时候传一部分,最终形成一个整体作为实参参与运算
function fun (a,b) {
console.log(this);//指向window
console.log(a+b);
}
// 函数内部在调用的时候,this有自己默认的指向
// console.dir(fun);
// fun(3,4);
// call方法
// 1.功能:第一个可以指定函数的 this,第二个可以执行函数并传参
// 2.参数:第一个参数传入指定让 this 指向的对象,第二个参数及以后,是函数参数的列表
// 3.返回值:就是函数自己的返回值
// 4.测试
var o = {
name : "zj"
};
// fun.call(o,1,2);//this指向对象 o
// apply方法
// 1.功能:第一个可以指定函数的 this,第二个可以执行函数并传参
// 2.参数:第一个参数传入指定让 this 指向的对象,第二个参数是函数的参数组成的数组
// 3.返回值:就是函数自己的返回值
// 4.测试
// fun.apply(o,[4,5]);
// bind方法
// 1.功能:第一个可以指定函数的 this,bind方法不能执行函数,但是可以传参数
// 2.参数:第一个参数传入指定让 this 指向的对象,第二个参数及以后,是函数参数的列表
// 3.返回值:返回一个新的指定了 this 的函数,也可以叫绑定函数
// 4.测试
var fn = fun.bind(o,2,3);
// console.log(fn);//已经绑定给 o 了
fn();//可以不传参数,直接调用
fn(6,7);//传了参数也没用
// 当然,如果原函数是4个参数,传入6,7就可以把他们当做实参参与运算
4.函数的其他成员
• arguments 实参集合
• arguments.callee 函数本身,arguments的一个属性
• fn.caller 函数的调用者,如果在全局调用,返回的调用者为 null。
• fn.length 形参的个数
• fn.name 函数的名称
// 看一下函数内部的成员
// 看一下函数内部的成员
function fn(a,b) {
// 实际应用中,会在函数内部直接使用 一个 arguments 的关键字
console.log(arguments);
// console.log(arguments.callee);
// 存储的是函数在调用时,传入的所有 实参 组成的一个类数组对象
console.log(fn.arguments);
// 函数的调用者,函数在哪个作用域调用,caller 就是谁,如果在全局调用,值就是 null
console.log(fn.caller);
// length 指的是形参的个数
console.log(fn.length);
// 函数的名字
console.log(fn.name);
}
// function test() {
// fn(1,2,3,4);
// }
// test();
// 灵活使用 arguments 类数组对象,可以记录所有的实参
// 模仿制作一个max方法
function max() {
// 判断实参中最大的数
var nowMax = arguments[0];
for (var i = 1 ; i < arguments.length;i++) {
if (arguments[i] > nowMax) {
nowMax = arguments[i];
}
}
return nowMax;
}
console.log(max(1,4,7,9));
5.高阶函数
// 高阶函数
// 1.函数作为另一个函数的参数
// 定义一个函数,吃饭的函数,吃完后,可以去做其他事,看电影,聊天,看书
function eat(fn) {
console.log("吃晚饭");
// 接下来的要做的事情是不固定的
fn();
}
eat(function() {
console.log("看电影");
});
// 2.函数作为一个函数的返回值
// 需求:通过同一段代码实现以下效果
// 输出: 100 + m
// 输出:1000 + m
// 输入:10000 + m
function outer(n) {
function inner (m) {
console.log(m + n);
}
}
// 在外部执行 inner函数
// 100 + m
var fun = outer(100);
fun(230);
fun(50);
var fun1 = outer(1000);
fun1(55);
6.函数闭包
函数定义时天生能记住自己生成的作用域环境(变量)和函数自己,将他们形成一个密闭环境,这就是闭包。无论函数以任何方式在任何地方调用,都会回到自己定义时的密闭环境进行执行。
- 简单来讲,就时函数能使用其定义在其外的变量,延伸了变量的使用范围。
// 体会闭包
// 将一个内部函数拿到复函数外面,观察是否还能调用复函数内部的变量
function outer() {
var a = 10;
function inner() {
console.log(a);
}
// 将inner函数作为返回值
// inner();
return inner;
}
// outer外面,是不能直接访问a变量
// outer();
// console.outer(a);
// 将 outer 执行的结果,赋值给一个变量
var inn = outer();
console.log(inn);
// 在全局调用inn,按道理应该查找全局的 a 变量
inn();
// 输出的真正结果是 10, 来自于 outer 函数内部的变量
<script>
// 体会闭包
// 将一个内部函数拿到复函数外面,观察是否能调用复函数内部的变量
function outer() {
// 闭包形成的闭包环境中,变量不是一成不变的,可以被更改
var a = 10;
function inner() {
console.log(a++);
}
// 将inner 函数作为返回值
return inner;
}
var inn = outer();
inn();
inn();
</script>
三、ES6新特性
1.let与块级作用域
<script>
// if(true) {
// let foo = 1;
// }
// console.log(foo);
// 可以通过新的关键字 let 定义块内部的变量
// 通过let 定义的变量在块级作用域内能被访问
// 非常适合设置 在 for 循环中的循环变量
// for(var i = 0; i< 3; i++) {
// for(var i = 0;i <3; i++) {
// console.log(i);
// }
// }
// 通过let 定义变量,只在自己的循环中生效
// for(let i = 0; i < 3; i++) {
// for(let i = 0;i < 3; i++) {
// console.log(i);
// }
// }
// 通过循环批量添加事件
// var ele = [{},{},{}];
// for (let i = 0; i < ele.length ; i++) {
// ele[i].onclick = function () {
// console.log(i);
// }
// }
// ele[0].onclick();
// 循环 实际上有两层作用域
// for (let i = 0; i < 10; i++) {
// let i = "foo";
// console.log(i);
// }
// 和var 的另一个区别, let不会进行变量声明提升
console.log(a);
// var a = 1;
let a = 2;
</script>
2.const恒量\常量
一旦赋值不可修改,且不可先定义后期再赋值。
<script>
// 不能改变值,是指不能指向一个新的内存地址
// const name = "zs";
// name = "ls";//Uncaught TypeError: Assignment to constant variable.
// 声明的时候必须同时赋初始值
// const name;
// name = "cs";//Uncaught SyntaxError: Missing initializer in const declaration
const obj = {};
obj.name = "sl";//允许
obj = {};//不允许,因为这是开辟了新内存
</script>
var,let和const的使用规律
- 不用var ,
- 主用const :后期不能被更改的数据
- 配合let:后期可更改的数据
3.数组解构与对象解构
数组解构--获取数组内部项
<script>
// 数组解构
// 普通的方法获取内部项
// const arr = [100, 200, 300]
// const foo = arr[0]
// const bar = arr[1]
// const baz = arr[2]
// console.log(foo, bar, baz)
// 解构
// const arr = [100, 200, 300]
// const [foo, bar, baz] = arr
// console.log(foo,bar,baz);
// 获取哪一项,就写哪一项,但是前面用逗号隔开
// const arr = [100, 200, 300]
// const [, , baz] = arr
// console.log(baz);
//获取从foo 之后的剩余项,这些项会存到rest中形成数组
// const arr = [100, 200, 300]
// const [foo, ...rest] = arr
// console.log(rest);//(2) [200, 300]
// 可以少写,从头开始解构
// const arr = [100, 200, 300]
// const [foo] = arr
// console.log(foo) //100.
// 多写了会出现undefined,但是可以单独给多写的赋值
// 如果数组有值可以解构到,就赋数组的值,如果没有,就赋当前的值
// const arr = [100, 200, 300]
// const [foo, bar, baz = 400, more = 123] = arr
// console.log(more);//123
// console.log(baz);//300
// 应用
const path = "foo/bar/baz"
// 普通方法就是
// const temp = path.split("/");
// const a = temp[1];
// console.log(a);//bar
// 解构方法
const[,,a] = path.split("/")
console.log(a)//baz
</script>
对象解构--获取对象内部属性
<script>
// 对象里没有数组里的次序,所以解构更简单一些
obj = {name: "zs", age: 18}
const {name} = obj//必须和原有属性值相同
console.log(name);
// 解决解构命名冲突
const name = "tom"
const {name:newName = "javl"} = obj//没有默认值的输出javl
console.log(newName);
console.log(name);
// 应用
const {log} = console
log("hah");
</script>
4.模板字符串与应用
// 模板字符串用 `` 表示
// const str = `this is
// a \`string`
// console.log(str);
// 支持插值表达式,在字符串中插入值、
//可以插入符合js语法的任何语句
const name = "tom"
const str = `hello,${name},${1 + 1},${Math.random()}`
console.log(str);
模板字符串标签函数
<script>
// 模板字符串标签函数
// const str = console.log`hello, wangxiaoming`
const name = "zss"
const gender = false
// 主要是用来操作字符串,获得更需要的字符串形式
function myTagFunc (strings, name, gender) {
// console.log(strings, name, gender);
const sex = gender ? "man" : "woman"
return strings[0] + name + strings[1] + sex + strings[2]
}
const str = myTagFunc`hi,${name} is a ${gender}`
console.log(str);
</script>
5.字符串扩展方法
- startWith()
- endsWidth()
- Includes()
<script>
// Array.from() 将伪数组转换为真数组
var arr Array.from(divs,item => item * 2);
// find 查找第一个满足条件的值
let arr = [{
id:1,
name: '慕寒'
},{
id:2,
name: 'Vk'
}];
let res = arr.find(item => item.id == 2);
console.log(res);
// findIndex 找出第一个符合条件的索引
var arr = [10,20,50];
let res = arr.findIndex(item => item > 15);
console.log(res); // 1
const msg = 'Error: foo is not defined.'
console.log(msg.includes('error'));
console.log(msg.startsWith('Error'));
console.log(msg.endsWith('.'));
</script>
6.参数默认值
<script>
// 函数参数默认值,赋默认值的参数要放到后面,防止前面的值没有传参数
function fun(enable = true) {
console.log('foo invoked enable:');
console.log(enable);
}
fun("bar",false);
</script>
<script>
// 展开数组操作
const sarr = ["bar","foo","baz"]
console.log(sarr[0], sarr[1], sarr[2]);
console.log(...sarr);
</script>
7.箭头函数
- 箭头函数的this指向:
箭头函数内部不会改变this的指向,没有this机制,查找this的时候,都会到函数外部寻找this
<script>
// 箭头函数
// const plus = (a,b) =>{
// console.log('plus revoked');
// return a+b
// }
// console.log(plus(1,3));
const arr = [1,2,3,4,5,6,7]
const arr1 = arr.filter(
function (item) {
return item % 2//此处返回值0或1,0不能返回,1可以,所以返回的就是奇数项
}
)
console.log(arr1);
const arr1 = arr.filter(i => i % 2)//[1, 3, 5, 7]
// 箭头函数与this
const person = {
name: "tom",
// sayHi: function () {
// console.log(`hi,my name is ${this.name}`);
// }//hi,my name is tom
// sayHi: () => {
// console.log(`hi,my name is ${this.name}`);
// }//hi,my name is
sayHi: function () {
// const _this = this;
setTimeout(() => {
console.log(`hi,my name is ${this.name}`);
},1000);
}
}
person.sayHi()
</script>
8.对象字面量增强
<script>
const bar = 'bar'
const age = "123"
obj = {
name: "zhangsa",
bar,
sayHi () {
console.log("hi");
console.log(this);//调用者
},
[age]: 18
}
// obj[age] = 18
console.log(obj);
</script>
9.object.assign方法
<script>
// // 对象扩展方法
// // Object.assign方法
// const source1 = {
// a:123,
// b:123
// }
// const source2 = {
// b:678,
// d:789
// }
// const target = {
// a: 456,
// c: 789
// }
// const result = Object.assign(target,source1)
// const result2 = Object.assign(target,source1,source2)
// console.log(target);//{a: 123, c: 789, b: 123}
// console.log(target === result);//true
// 复制对象
// function fun (obj) {
// // 希望内部更改时,不要改变外部对象
// const newObj = Object.assign({},obj);
// newObj.name = "tom",
// console.log(newObj);
// }
// const obj = {
// name: "jack",
// age: 18
// }
// fun(obj)//{name: 'tom', age: 18}
// console.log(obj);//{name: 'jack', age: 18}
// 应用 ,在options对象接收时,简化
function Block(options) {//构造函数,复制对象
// this.width = options.width
Object.assign(this,options)
}
const block1 = new Block ({width:100,height:100,
x:40,y:240})
console.log(block1);
</script>
新增数据解构
10.set数据结构(不允许重复)
可用于数组去重
<script>
// Set数据结构
const s = new Set()
s.add(1).add(2).add(2).add(4).add(5)
// console.log(s);
// s.forEach(i => console.log(i))
// for (let i of s) {
// console.log(i);
// }
// console.log(s.size);
// console.log(s.has(4));
// console.log(s.delete(3));
// console.log(s);
// s.clear()
// console.log(s);
// 给数组去重
const arr = [1.3,4,6,2,4,7,5,8];
// const b = new Set(arr)
// const b = Array.from(new Set(arr))
const b = [...new Set(arr)]//将Set展开
console.log(b);
</script>
11.MAP数据结构
<script>
// Map 数据结构
const obj = {}
obj[true] = "boolean"
obj[123] = "number"
obj[{a: 1}] = "object"
// console.log(Object.keys(obj));//输出键,全部变为字符串
// console.log(obj[{}]);
// console.log(obj['[object Object]']);
const map = new Map()
const a = {a:1}
map.set(a,100)
console.log(map);
console.log(map.get(a));
// map.has()
// map.clear()
// map.delete()
map.forEach((value,key) => {
console.log(key,value)
})
</script>
12.symbol类型
最主要的作用就是为对象添加独一无二的属性标识符
// Symbol补充
console.log(Symbol("fu") === Symbol("fu"));
// 重复使用symbol,使用全局变量
const a = Symbol.for(true)//布尔会转成字符
const b = Symbol.for("true")
console.log(a === b);//true
// 内置symbol常量
// const obj = {
// [Symbol.toStringTag]: "xojjdlag"
// }
// console.log(obj.toString());//[object xojjdlag]
const obj ={
[Symbol()]: "symbol value",
foo: "foo value"
}
console.log(Object.keys(obj));// ['foo']
for(var k in obj) {
console.log(k);//foo
}
console.log(JSON.stringify(obj));//{"foo":"foo value"}
console.log(Object.getOwnPropertySymbols(obj));//[Symbol()]
</script>
13.for of 循环
<script>
// for……of 循环
// const arr = [100, 200, 300, 400]
// for (const item of arr) {
// console.log(item);
// }
// arr.forEach(item => { 没有办法打断遍历
// console.log(item)
// })
// for (const item of arr) {
// console.log(item)
// if (item >= 200) {
// break
// }
// }
// const s = new Set(["foo", "bar", "baz"])
// for (const item of s) {
// console.log(item)
// }
// const m = new Map()
// m.set("foo",1)
// m.set("bar",2)
// for (const [key,value] of m) {
// console.log(key,value)
// }
// 不可用来遍历对象
// const obj = {
// name: "zs",
// age: 18
// }
// for (const item of obj) {
// console.log(item)
// }
</script>
四、正则表达式
1.构造方式
1.使用一个正则表达式字面量,如下所示:
var reg = /abc/;
2.调用 RegExp 对象的构造函数,如下所示:
var re = new RegExp("abc");
第二种性能较差
2.正则方法
字符串方法
<script>
// split方法:分割字符串,成为一个数组
// var str = 'aa bbb c dd eeeeee';
// // 使用一个空字符进行的精确分割
// // var arr = str.split(" ");
// // 使用正则表达式可以进行模糊匹配分割
// var arr1 = str.split(/\s+/);
// // console.log(arr1);
// var str2 = 'bbaacaaaadddaeeeeeaaf';
// var arr2 = arr2.split(/a+/);
// console.log(arr2);
// search方法:查询子串在父字符串中出现的位置
// var str = "abcdefg";
// console.log(str.search(/ef/));//找到返回第一次出现的下标,找不到返回-1
// match方法:在父字符串中匹配符合的子字符串,将结果输出到数组中
var str = 'abbcccbbbbbddbbbdabbb';
//var arr = str.match(/b+/);//匹配到第一个后结束
// var arr = str.match(/b+/g);//全局修饰符 g,会在找到所有匹配字符串后结束
// console.log(arr);
// replace方法:替换字符串的指定部分,返回值是一个新的替换后的字符串,原始字符串不受影响
var str = "www.hello.com";
var str1 = str.replace(/hello/,'byebye');
console.log(str);
console.log(str1);
</script>
正则表达式方法;
// exec方法:查找匹配的字符串,输出到数组中,返回一个数组
// 不论有没有全局修饰符,都只会找到第一个后结束
var str = 'aaaabccccbacabc';
var reg = /abc/;
var arr = reg.exec(str);
console.log(arr);
// test方法:检测字符串中是否满足正则表达式的匹配规则,返回布尔值
var reg = /abc/;
console.log(reg.test('aaaabccccbacabc'));
特殊字符
- 特殊字符:javascript 中常用特殊字符有 ( ) [ ] { } \ ^ $ | ? * + .
- 若想匹配这类字符必须用转移符号 \ 如:(,^,\
预定义特殊字符 | 说明 | 举例 |
---|---|---|
\t | 制表符 | /\t/ |
\n | 回车符 | /\n/ |
\f | 换页符 | /\f/ |
\b | 空格 | /\b/ |
边界符 | 说明 | 举例 |
---|---|---|
^ | 开头 不能紧跟于左中括号的后面 | /^hello/.test('hello javascript') => true |
$ | 结尾 | /javascript$/.test('hello javascript') => 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次(+的升级版) |
? | 软性量词 | 出现零次或一次 |
* | 软性量词 | 出现零次或多次(任意次) |
+ | 软性量词 | 出现一次或多次(至少一次) |
分组
小括号表示分组()
// 分组
var reg = /^(bye){2}$/;
console.log(reg.test("byebye"));
console.log(reg.test("bbyyee"));//false
或操作符 |
可以使用竖线(|)字符表示或者的关系。
/a|bcd/ 匹配 a 或 bcd 字符。
/(ab)+|(cd)+/ 匹配出现一次或多次的 ab 或者 cd
<script>
// 或操作符 使用竖线 | 字符表示或的关系
var reg = /^a|bcd$/; //匹配时,要么匹配以 a开头的,要么匹配以 bcd 结尾的
console.log(reg.test("asbcd"));
console.log(reg.test("sbcd"));
// 如果想在正则中,两个规则之间只能选其中一个,不能包含其他开头结尾,需要去将或运算放到分组里
var reg = /^(ab|cd)$/;
console.log(reg.test("abcd"));//false
console.log(reg.test("ab"));//true
console.log(reg.test("cd"));//true
</script>
举例
分组反向引用
// 正则中通过分组匹配到字符串中,会被进行编号,从1开始
// 在正则内部可以通过 \1 方式,去对字符串进行反向引用
console.log(/^([a-z]{3})\1$/.test("byebye"));//true
console.log(/^([a-z]{3})\1$/.test("byebye"));//false
// 正则表达式以外通过 $1,进行字符串的引用
// var str = "123*456".replace(/^(\d{3})\*(\d{3})$/,"$2*$1");
// console.log(str);
// 第二个参数可以是一个函数
var str = "123*456".replace(/^(\d{3})\*(\d{3})$/,function(match,$1,$2){
return $2 * 2 + "/" + $1 * 3;
});
中文字符
匹配中文:
[\u4e00-\u9fa5]
<script>
// 匹配中文字符
var reg = /^[a-z\u4e00-\u9fa5]+$/;
console.log(reg.test("只有中文的文字内容"));
console.log(reg.test("只有中文的a文字内容"));
console.log(reg.test("只有中文的 文字内容"));
</script>