1.JS基础-对象Object
通过 JavaScript,您能够定义并创建自己的对象。
创建新对象有两种不同的方法:
- 使用 Object 定义并创建对象的实例。
- 使用函数来定义对象,然后创建新的对象实例。
在 JavaScript 中,几乎所有的对象都是 Object 类型的实例,它们都会从 Object.prototype 继承属性和方法。
Object 构造函数创建一个对象包装器。
Object 构造函数,会根据给定的参数创建对象,具体有以下情况:
- 如果给定值是 null 或 undefined,将会创建并返回一个空对象。
- 如果传进去的是一个基本类型的值,则会构造其包装类型的对象。
- 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址。
- 当以非构造函数形式被调用时,Object 的行为等同于 new Object()。
1.1创建对象的方法
-
new Objcet
person=new Object(); person.firstname="John"; person.lastname="Doe"; person.age=50; person.eyecolor="blue"; -
键值对
// 同上,也是使用 Object来创建对象 // JavaScript 对象就是一个 name:value 集合 person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"}; -
使用对象构造器
// 本例使用函数来构造对象 function person(firstname,lastname,age,eyecolor) { this.firstname=firstname; this.lastname=lastname; this.age=age; this.eyecolor=eyecolor; }
1.2 把方法添加到 JavaScript 对象
方法只不过是附加在对象上的函数。
在构造器函数内部定义对象的方法:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
function changeName(name)
{
this.lastname=name;
}
}
1.3 JavaScript 类
JavaScript 是面向对象的语言,但 JavaScript 不使用类。
在 JavaScript 中,不会创建类,也不会通过类来创建对象(就像在其他面向对象的语言中那样)。
JavaScript 基于 prototype,而不是基于类的。
2.JS基础-原型链
2.1 constructor 的含义
constructor 是一种用于创建和初始化class创建的对象的特殊方法。
在一个类中只能有一个constructor方法,本质是一个构造函数,如果一个类不指定一个构造函数(constructor)方法, 则使用一个默认的构造函数(constructor)。
所谓“构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。
- 一个类必须有
constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。
示例代码:
/* 1. ***************************************/
// 定义了一个空的类Point,
class Point {
// JavaScript引擎会自动为它添加一个空的constructor方法
}
// 等同于
class Point {
constructor() {}
}
/* 2.***************************************/
// 首先声明一个 People 类
class People{
constructor(sex,age) {
this.name = '三上悠亚';
this.sex = sex;
this.age = age;
}
}
const people1= new People();
console.log(people1.name); //三上悠亚
people1.constructor // === People
//class People{
// constructor(sex,age) {
// this.name = '三上悠亚';
// this.sex = sex;
// this.age = age;
// }
//}
/* 3.***************************************/
var txt = "Hello World!";
txt.constructor // ƒ String() { [native code] }
-
constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor属性,防止引用的时候出错。
function Person(name) {
this.name = name;
}
Person.prototype.constructor === Person // true
Person.prototype = {
method: function () {}
};
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true
在JavaScript中,prototype对象是实现面向对象的一个重要机制。
每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。
在通过new创建一个类的实例对象的时候,prototype对象的成员都成为实例化对象的成员。
1、该对象被类所引用,只有函数对象才可引用;
2、在new实例化后,其成员被实例化,实例对象方可调用。
同时,函数是一个对象,函数对象若直接声明成员,不用被实例化即可调用。
2.2 原型对象prototype
我们首先总结一下原型对象的作用:
- 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中去。
- 当我们访问对象的一个属性或者方法时,它会先在对象自身中寻找,如果找到要查找的属性或者方法就会直接使用,如果没有就会去原型对象中查找,找到了就会直接使用。
- 在我们创建构造函数时,可以将对象的共有属性或方法,统一添加到构造函数的原型对象中去。这样做的好处就是不用分别为每一个对象添加这些共有的属性或方法,也不会影响到全局作用域,同时每个对象也都具有这些属性和方法。可以说给原型对象添加属性或者方法,函数的所有实例对象都会自动拥有原型中的属性和方法。
- 普遍来说,对于所有函数,每个函数是都会含有一个prototype属性,它默认指向一个object空对象(也称为原型对象)。
- 原型对象中都有一个constructor属性,它是指向函数对象的
function fun(){
console.log('fun')
}
console.log(fun.prototype) //默认指向一个Object空实例对象,没有我们的属性
//原型对象中都有一个constructor属性,它指向函数对象
console.log(fun.prototype.constructor === fun) //true
2.3 prototype 和 __proto__
-
每个函数都有prototype(显式原型)和
__proto__(隐式原型); -
每个对象/构造器函数的实例(这个也是对象)都有
__proto__; -
实例的
__proto__指向构造函数的prototype。这个称为构造函数的原型对象; -
js引擎会沿着
__proto__->prototype的顺序一直往上方查找,找到window.Object.prototype为止,Object为原生底层对象,到这里就停止了查找,如果没有找到,就会报错或者返回undefined;
-
而构造函数的
__proto__指向Function.prototype f() {[native code]}【构造器函数,但这个叫法并不准确,它目前没有一个合适的中文名】; -
__proto__是浏览器厂商实现的,W3C规范中并没有这个东西;
-
所有对象都具有__proto__是过去具有,未来将移除,因为规范未定义,只是浏览器实现的一个get属性。

-
JS代码还没运行的 时候,JS环境里已经有了一个window对象。函数是对象;
-
window对象有一个Object属性,window.Object是一个函数对象;
-
window.Object这个函数对象有一个重要属性是prototype;
-
window.Object.prototype里面有一堆属性;
-
所有的实例函数
__proto__都会指向构造函数的prototype; -
constructor是反向的prototype;
再复杂一下,arr.valueOf() 做了什么?
-
arr自身没有 valueOf,于是去arr.__proto__上找; -
arr.__proto__只有 pop、push 也没有 valueOf,于是去arr.__proto__.__proto__ 上找; -
arr.__proto__.__proto__ 就是 window.Object.prototype; - 所以 arr.valueOf 其实就是 window.Object.prototype.valueOf;
- arr.valueOf() 等价于 arr.valueOf.call(arr)
- arr.valueOf.call(arr) 等价于 window.Object.prototype.valueOf.call(arr)
3.JS基础-函数进阶
函数即对象
首先:JS所有事物都是对象,对象是拥有属性和方法的数据。所以函数也是对象。
当创键一个函数的时候,发生了什么?
实际上,函数是Function类型的实例,此时可以把每一个创建出来的函数,当成是Function类型的实例对象,所以函数本身拥有的对象属性,来源于Function。
Fn.Contructor几位Function
但是与此同时要注意:Function.prototype.__proto__===Object.prototype
可以理解为:构造器函数的构造函数是Object
也可以简单的理解:函数即对象。
构造函数
- 在 JavaScript 中,用 new 关键字来调用的函数,称为构造函数。构造函数首字母一般大写;
- 构造函数的执行过程:
(1) 当以 new 关键字调用时,会创建一个新的内存空间,标记为 Animal 的实例;
(2) 函数体内部的 this 指向该内存,每当创建一个实例的时候,就会创建一个新的内存空间;
(3) 给 this 添加属性,就相当于给实例添加属性;
(4) 由于函数体内部的this指向新创建的内存空间,默认返回 this ,就相当于默认返回了该内存空间;
//举个例子,我们要录入一年级一班中每一位同学的个人信息,那么我们可以创建一些对象:
var p1 = {name: 'zs', age:6, gender: '男', hobby: 'basketball'};
var p2 = {name: 'ls', age:6, gender: '女', hobby: 'dancing'};
var p3 = {name: 'ww', age:6, gender: '女', hobby: 'singing'};
var p4 = {name: 'zl', age:6, gender: '男', hobby: 'football'};
//我们观察到,这四个对象结构类似,甚至如参数age也是相同的,那么,可以使用构造函数
function Person(name, gender, hobby) {
this.name = name;
this.gender = gender;
this.hobby = hobby;
this.age = 6
}
var p1 = new Person('zs', '男', 'baketball')
自执行匿名函数
声明式会导致函数提升,function会被解释器优先编译。即我们用声明式写函数,可以在任何区域声明,不会影响我们调用
//声明式
function XXX() { }
函数表达式函数表达式我们经常使用,而函数表达式中的function则不会出现函数提升。而是JS解释器逐行解释,到了这一句才会解释。因此如果调用在函数表达式之前,则会调用失败。
var fn1 = function(){}
fn1();
function fn1(){}//可以正常调用
fn2();
var fn2 = function(){}//无法调用
匿名函数类型:
//普通匿名函数,无返回值
>(function () {
alert('你妈逼')
})()
<undefined
//感叹号开头,返回布尔值
>!function () {
alert('你妈逼')
}()
<true
//~function(){}()
>~function(){}()
<-1
//-function(){}()
>-function(){}()
<NaN
//+function(){}()
>+function(){}()
<NaN
4.JS基础-面向对象
封装
封装:把客观事物封装成抽象的类,隐藏属性和方法,仅对外公开接口。
在ES6之前,是不存在class这个语法糖类的。所以实现大多采用原型对象和构造函数
私有属性和方法:只能在构造函数内访问不能被外部所访问(在构造函数内使用var声明的属性)
公有属性和方法(或实例方法):对象外可以访问到对象内的属性和方法(在构造函数内使用this设置,或者设置在构造函数原型对象上比如Cat.prototype.xxx)
静态属性和方法:定义在构造函数上的方法(比如Cat.xxx),不需要实例就可以调用(例如Object.assign())
function Person(name) {
this.name = name;
this.sex = 'man';
var evil = '我问你干爹';
var pickNose = function(){
console.log('我我我我')
}
return name
}
在ES6之后,存在class这个语法糖类。当你使用class的时候,它会默认调用constructor这个函数,来接收一些参数,并构造出一个新的实例对象(this)并将它返回,因此它被称为constructor构造方法(函数)。
以下两个对象等价(第二个命名有问题啊??):
class Cat{
constructor(){}
toString(){}
toValue(){}
}
function Cat(){}
Cat.prototype = {
constructor(){}
toString(){}
toValue(){}
}
继承
继承:继承就是子类可以使用父类的所有功能,并且对这些功能进行扩展。
比如我有个构造函数A,然后又有个构造函数B,但是B想要使用A里的一些属性和方法,一种办法就是让我们自身化身为CV侠,复制粘贴一波。还有一种就是利用继承,我让B直接继承了A里的功能,这样我就能用它了。
- 原型链继承
- 构造继承
- 组合继承
- 寄生组合继承
- 原型式继承
- 寄生继承
- 混入式继承
- class中的extends继承
原型链继承:
function Parent(){
this.name = 'Parent';
this.sex = 'box';
}
function Child(){
this.name = 'Child'
}
Child.prototype = new Parent()
var child1 = new Child()
instanceof
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
A instanceof B 实例对象A instanceof 构造函数B检测A的原型链(proto)上是否有B.prototype,有则返回true,否则返回false
class中的extends继承:
class PParent{
constructor(name){
this.name = name
}
getName(){
console.log(this.name)
}
}
class CChild extends PParent{
constructor(name){
super(name)
this.sex = 'boy'
}
}

多态
class中的多态:extends、super
多态的实际含义是:同一操作作用于不同的对象上,可以产生不同的解释和不同的执行结果。
对于js多态的详细解释:https://juejin.cn/post/6844904126011146254
/*
instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置。
简单理解为:instanceof可以检测一个实例是否属于某种类型。
还可以在继承关系中用来判断一个实例是否属于它的父类型。
*/
function makeSound(animal){
if (animal instanceof Cat){
console.log("喵喵喵")}
else if (animal instanceof Dog){
console.log("汪汪汪")}
}
class Cat{}
class Dog{}
makeSound(new Cat())
// < 喵喵喵
makeSound(new Dog())
// < 汪汪汪
5. JS基础-this与new

this,是指当前的本身,在非严格模式下this指向的是全局对象window,而在严格模式下会绑定到undefined。
this的5种绑定方式:
默认绑定
非严格模式下this指向全局对象, 严格模式下this会绑定到undefined
var a = 10;
// undefined
function foo() {
console.log(this.a)
}
foo
/* f foo() {
console.log(this.a)
} */
foo()
/*
VM245:2 10
undefined
*/
window.a
// 10
严格模式下:
"use strict"
var a = 10;
function foo() {
console.log('this1', 'this')
console.log(window.a)
console.log(this.a)
}
console.log(window.foo)
console.log('this2', this);
foo()
开启了严格模式,只是说使得函数内的this指向undefined,它并不会改变全局中this的指向。因此this1中打印的是undefined,而 this2还是window对象;另外,它也不会阻止a被绑定到window对象上
let a = 10;
const b = 20;
function foo (){
console.log(this.a)
console.log(this.b)
}
foo();
console.log(window.a)
var a = 1;
function foo(){
var a = 2;
console.log(this)
console.log(this.a)
}
foo()
var a = 1;
function foo () {
var a =2;
function inner () {
console.log(this.a)
}
inner()
}
foo()
隐式绑定
当函数引用有上下文对象时, 如 obj.foo()的调用方式, foo内的this指向obj
function foo () {
console.log('this.a:', this.a)
console.log('this:', this)
}
var obj = { a: 1, foo}
var a = 2
obj.foo()
// 相当于:
var obj = {
a :1,
foo: function () {
console.log(this.a)
}
}
var a = 2
obj.foo()
语法糖:
var obj == {foo}
等同于
var obj = { foo: foo }
隐式丢失:隐式丢失其实就是被隐式绑定的函数在特定的情况下会丢失绑定对象。
两种情况:
1.使用另一个变量来给函数起别名,将函数作为参数传递时会被隐式赋值,回调函数丢失this绑定。
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo};
var a = 2;
var foo2 = obj.foo;
obj.foo();
foo2();
原因:虽然foo2 指向obj.foo,但是调用的时候是window对象在调用,所以this的指向也是window在使用,即this.a等同于window.a
下一个例题:
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a:3, foo2: obj.foo }
obj.foo();
foo2();
obj2.foo2();
猜一下?
obj.foo(); --> 1
foo2(); --> 2
obj2.foo2(); --> 3
哦,蒙对了嗷!流批。
原因:
- obj.foo()中的this指向调用者obj;
- foo2()发生了隐式丢失,调用者是window,使得foo()中的this指向window;
- foo3()发生了隐式丢失,调用者是obj2,使得foo()中的this指向obj2
下一个例题:
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
开始猜:
我猜是 2,局域变量的应该是从doFoo开始的,此处没有传入(定义)a的值,所以a应该是window.a=2,后面打印值应该也是这个值
下一个例题:
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
是3呗?
我靠恁娘... ...
我们打印个详细的看看结果:
打印3是doFoo干的,此时还打印了this,显示的值就是上面图中的对象,可见a就是3;
打印2是obj.foo干的,此时的this已经是window对象了,打印a就是2,不行见图:
总结:
所以说,如果你把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined。
显示绑定
通过call()或者apply()方法直接指定this的绑定对象, 如foo.call(obj)
call, apply, bind
- 都是对函数的操作,使用方式:函数.call
- 都是用来改变函数的 this 对象的指向的。
- 第一个参数都是 this 要指向的对象。
- 都可以利用后续参数传参。
- call 接受函数传参方式为:fn.call(this, 1, 2, 3)
- apply 接受函数传参方式为:fn.apply(this,[1, 2, 3])
- bind 的返回值为一个新的函数,需要再次调用: fn.bind(this)(1, 2, 3)
三点特性:
-
使用
.call()或者.apply()的函数是会直接执行的; -
bind()是创建一个新的函数,需要手动调用才会执行; -
.call()和.apply()用法基本类似,不过call接受若干个参数,而apply接受的是一个数组.call(this, 1, 2, 3, 4, 5) .apply(this, [1, 2, 3, 4, 5])
可以看下菜鸟教程和W3C的教程:
JavaScript 函数调用 | 菜鸟教程 (runoob.com)
JavaScript 函数 Call (w3school.com.cn)
JavaScript 函数 Apply (w3school.com.cn)
JavaScript Function bind() (w3school.com.cn)
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2
foo() //2
foo.call(obj) //1
foo.apply(obj) //1
foo.bind(obj) //此处打印函数对象,()调用时才会运行打印
升级:
var obj1 = {
a: 1
}
var obj2 = {
a: 2,
foo1: function () {
console.log(this.a)
},
foo2: function () {
setTimeout(function(){
console.log(this)
console.log(this.a)
}, 0)
}
}
var a = 3
obj2.foo1()
obj2.foo2()
obj2.foo1()打印2;
obj2.foo2(), setTimeout是window的方法,等于window.setTimeout,所以此时的this.a指的是window.a
一句话:this永远指向最后调用它的对象
如果想通过call,apply,bind方法,将a绑定到对象方法中,不打印全局的a,怎么完成?
obj2.foo2.call(obj2)
//这样行吗?
如果是这种写法的话,那么改变的就是foo2函数内的this指向了,但是我们知道,foo2的指向和setTimeout里函数的this是没有关系的,i那位调用定时器的始终是window。
var obj1 = {
a: 1
}
var obj2 = {
a: 2,
foo1: function () {
console.log(this.a)
},
foo2: function () {
setTimeout(function(){
console.log(this)
console.log(this.a)
}.call(obj1), 0)
}
}
var a = 3
obj2.foo1()
obj2.foo2()
下一个:
var obj1 = {
a: 1
}
var obj2 = {
a: 2,
foo1: function () {
console.log(this.a)
},
foo2: function () {
function inner () {
console.log(this)
console.log(this.a)
}
inner()
}
}
var a = 3
obj2.foo1()
obj2.foo2()

下一个:
var obj = {
name: 'obj',
foo1: () => {
console.log(this.name)
},
foo2: function () {
console.log(this.name)
return() => {
console.log(this.name)
}
}
}
var name = 'window'
obj.foo1()
obj.foo2()()
new绑定
- 如果 new 了函数内的 this 会指向当前这个 person 并且就算函数内部不 return 也会返回一个对象。
- 如果不 new 的话函数内的 this 指向的是 window。
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
return [this.firstname,this.lastname,this.age,this.eyecolor,this]
}
var myFather=new person("John","Doe",50,"blue");
var myMother=person("Sally","Rally",48,"green");
console.log(myFather) // this 输出一个 person 对象
console.log(myMother) // this 输出 window 对象
箭头函数绑定
ES6---箭头函数()=>{} 与function的区别_Run4Freedom的博客-CSDN博客
this的指向由外层作用域决定的
- 它里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时;
- 字面量创建的对象,作用域是window,如果里面有箭头函数属性的话,this指向的是window;
- 构造函数创建的对象,作用域可以理解为是这个构造函数,且这个构造函数的this是指向新建的对象的,因此this指向这个对象;
- 箭头函数的this是无法通过bind,call,apply来直接修改,但是可以通过改变作用域中的this的指向来间接修改
var name = 'window'
var obj = {
name: 'obj',
foo: function () {
console.log(this.name)
}
}
var obj2 = {
name: 'obj2',
foo: () => {
console.log(this.name)
}
}
构造函数中普通函数和箭头函数的区别
var name = 'window'
function Person(name){
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = () => {
console.log(this.name)
}
}
var person2 = {
name: 'person2',
foo2: () => {
console.log(this.name)
}
}
var person1 = new Person('person1')
person1.foo1() // person1
person1.foo2() // person1
person2.foo2() // window
箭头函数的this无法通过bind/call/apply来修改,但是可以通过改变作用域中的this的指向来间接修改
var name = 'window'
var obj1 = {
name: 'obj1',
foo1: function () {
console.log(this.name)
return() => {
console.log(this.name)
}
},
foo2: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.foo1.call(obj2)()
obj1.foo1().call(obj2)
obj1.foo2.call(obj2)()
obj1.foo2().call(obj2)
/*
obj2
obj2
obj1
obj1
window
window
window
obj2
*/
new的过程中发生了什么
- 新生成了一个对象
- 链接到原型
- 绑定 this
- 返回新对象
function create(...rest){
//创建一个空的对象
let obj = new Object()
//获得构造函数
let Con = rest.shift()
//链接到原型
obj.__proto__ = Con.prototype
//绑定this,执行构造函数
let result = Con.apply(obj, arguments)
//确保new出来是个对象
return typeof result === 'object' ? result : obj
}
6. JS基础-箭头函数
ES6标准新增了一种新的函数:Arrow Function(箭头函数)。
为什么叫Arrow Function?
因为它的定义用的就是一个箭头
x => x * x
相当于
function (x) {
return x * x;
}