new干了什么事
- 创建一个新对象
- 让新创建的子对象继承构造函数的原型对象(自动设置子对象的__proto__属性,指向构造函数的原型对象)
- 调用构造函数,将this指向这个新对象。在构造函数内为这个新对象添加对应的属性和方法
- 返回这个新对象的地址
this的指向情况
- obj.fun() fun中的this指向obj
- new Fun() Fun中的this指向创建的新对象
- 原型对象当中的this指向他的调用者
- fun()、匿名函数自调、回调函数中的this指向windows。严格模式下this为undefined
- DOM事件函数里的this指向当前正在触发事件的DOM元素对象
- Vue当中this默认都指向当前的vue对象
- 箭头函数的this指向当前函数之外最近作用域中的this(箭头函数底层原理为bind,bind是永久绑定,不可替代)
- call、apply、bind修改this
call、apply、bind
要调用的函数.call(替换this的对象,实参1,...)
call做的3件事
- 立刻调用一次点前的函数
- 自动将.前的函数中的this替换为指定的新对象
- 还要向要调用的函数中传实参值
要调用的函数.apply(替换this的对象,包含实参值的数组)
apply做的3件事
- 调用.前的函数
- 替换.前的函数中的this为指定的对象
- 拆散数组为多个元素值,分别传给函数的形参变量
var 新函数 = 原函数.bind(替换this的对象,不变的实参值)
bind做的2件事
- 创建一个和原函数一样的新函数副本
- 永久替换新函数中的this
- 永久替换部分形参变量为固定的实参值
用bind替换的this就不能再次替换this了
创建对象的方法
new Object()
缺点:步骤多
字面量
var 对象名 = {}
缺点:如果反复创建多个对象,代码很冗余
工厂函数方式
function obj(name,age){
var o = new Object()
o.name = name
o.age = age
o.say = function(){
console.log('工厂函数')
}
return o
}
var p = obj('nh',25)
缺点:本质还是Object(),将来无法跟据对象的原型对象准确判断对象的类型
构造函数方式
- 先用构造函数定义一类对象统一属性结构和方法
- 再用new调用构造函数,反复创建相同属性结构,不同属性值的多个对象
function Fn(name,age){
this.name = name
this.age = age
this.fn1=function(){...}
}
var a = new Fn('nh',25)
缺点:如果构造函数中包含方法,则重复创建,浪费内存
原型对象方式
先创建完全相同的对象,再给子对象添加个性化属性
function Fn(){}
Fn.prototype.name = 'nh'
Fn.prototype.age = 25
Fn.prototype.fn = function(){
....
}
var f = new Fn()
f.name = 'bh'//不会修改Fn原型上的name,只会在当前对象上添加一个name属性
混合模式
先创建完全相同的对象,再给子对象添加个性化属性
function Fn(name,age){
this.name = name
this.age = age
}
Fn.prototype.fn=function(){
console.log('Fn函数')
}
var a = new Fn('nh',25)
缺点:不符合面向对象封装的思想
动态混合模式
function Fn(name,age){
this.name = name
this.age = age
if(Fn.prototype.fn==="undefined"){
Fn.prototype.fn(){
console.log(this.name)
}
}
}
var a = new Fn('nh',25)
寄生构造
构造函数里调用其他构造函数
function Fn(name,age){
this.name = name
this.age = age
if(Fn.prototype.fn==="undefined"){
Fn.prototype.fn(){
console.log(this.name)
}
}
}
function Fn1(name,age){
var a = new Fn(name,age)
a.sex = 1
return a
}
var b =new Fn1('nh',25)
ES6 Class
class Fn{
constructor(name,age){
this.name = name
this.age = age
}
fn(){
console.log(this.name,this.age)
}
}
var a = new Fn('nh',25)
a.fn()
闭包,不用this,不用new!安全,可靠
function Fn(name,age){
var a = {}
a.getName = function(){
return name
}
a.setName = function(value){
name = value
}
return a
}
var b = Fn('nh',25)
console.log(b.getName())
继承
原型对象实现继承
构造函数中都有一个自带的属性prototype,指向自己配对的原型对象
let obj = {}
obj.prototype.属性名=属性值
obj.prototype.方法名=function(){...}
function Father() {}
var father = new Father();
Father.prototype.a = 1;
Father.prototype.fn = function () {
console.log('Father中函数');
}
//继承父类的实例
function Son() {}
// 子类原型继承父类的实例
Son.prototype = father;
var son = new Son()
console.log(son.a)
a.fn()
//通过 Son.prototype = father实现继承,改变子级的属性父级不会改变。但如果父级的圆形链上属性发生改变,子级的也会改变。
//继承父类的原型
function Son1() {}
Son1.prototype = Father.prototype;
var son = new Son1();
//原型的继承他们使用 同一个原型,会造成原型的污染
call方法继承(不会继承原型)
function Father(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
console.log('Father中函数');
}
function Son(name,age) {
Father.call(this,name,age)
}
var son = new Son('nh',25);
ES6当中的extends
class Father{
constructor(name,age){
this.name = name
this.age = age
}
fn(){
console.log('父级方法')
}
}
class Son extends Father{
constructor(name,age){
super(name,age)//调用父类构造方法
}
}
let son = new Son('nh',25)
console.log(son.name,son.age)
son.fn()
实例继承
function Father(name,age) {
this.name = name;
this.age = age;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
console.log('Father中函数');
}
function Son(name,age) {
var a = new Father(name,age)
a.sex = 1
return a
}
let b = new Son('nh',25)
组合继承
function Father(name,age) {
this.name = name;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
console.log('Father中函数');
}
function Son(name,age) {
Father.call(this)
this.age = age
}
Son.prototype = new Father()
Son.prototype.constructor = Son
var son = new Son()
原型链
- 由多级父对象逐级继承形成的链式结构。
- 保存着一个对象可用的所有属性和方法
- 控制着属性和方法的使用顺序(就近原则,先子集后父级)
ES6
模板字符串
${}里面可以放有返回值的合法的js表达式。不能放没有返回值的js表达式,也不能放分支/判断,循环等程序结构
let const
var问题:
- 声明提前
- 没有块级作用域 let、const:
- 不会声明提前
- 有块级作用域(底层会被翻译为匿名函数自调)
- 相同作用域内,静止声明两个同名的变量
- 全局声明变量,window当中找不到这个变量
- const声明为常量,不能修改实际原始类型的值和引用类型的地址,必须在声明的时候赋值
箭头函数
- this指向当前函数之外最近作用域中的this(箭头函数底层原理为bind,bind是永久绑定,不可替代)
- 构造函数不能使用
- 对象的方法不能使用
- 原型对象方法不能使用
- DOM中事件处理函数不能用
- 箭头函数无法使用call,apply,bind改变this
- 箭头函数不支持arguments
- 箭头函数没有prototype
for of
forEach只能遍历数字下标的索引数组,无法遍历类数组对象
for of
- 可以遍历数字下标的的索引数组以及类数组对象
- 无法获得下标位置i,只能获得元素值
- 无法控制遍历的顺序或步调,只能从头到尾,一个一个的顺序遍历
- 无法遍历下标名为自定义下标的对象和关联数组
- 只能遍历实现iterable的对象
- 遍历对象用for in,普通对象未实现iterable,for in遍历普通对象的每一项为对象的value,遍历数组为数组的下标
参数增强
- 参数默认值
- 剩余参数,...数组名
展开运算符
"..."
- 复制数组(浅拷贝)
- 合并数组
- 克隆对象(浅克隆)
- 合并多个对象和属性
解构
- 数组解构
- 对象解构
- 参数解构
Class&继承
- 直接放在class{}内的方法定义,其实还是保存在原型对象中的
- 在class中定义的属性,不会保存在原型对象中,而是成为每个子对象的自有属性
- 静态属性:不需要创建子对象,单靠类名就可以直接访问的属性。静态属性存在构造函数对象上的
extends:只是设置子类的原型对象继承父类型的原型对象、只能保证孙子对象可以使用爷爷类型原型对象中的共有方法、暂时无法为孙子对象补全缺少的自有属性。可以使用super关键字,super指向父类型
JS中的内置类型
- String、Number、Boolean
- Array、Date、RegExp
- Math(不是类型,是一个对象)
- Error
- Function Object
- global(全局作用域对象、在浏览器中被windows代替)