目录
文章目录
- 前言
- 一、什么是对象?
- 1. 对象创建的几种方式?
- 二、什么是原型以及原型链?
- 1.原型
- 2.原型链
- 三、继承的几种方式
- 1. 原型链继承
- 2.借用构造函数继承
- 3.寄生组合式继承
- 4.class类继承
- 四、this指向问题?
- 五、call()、apply()、bind()的区别?
- 六、对作用域链的理解?
- 七、new操作运算符做了什么(作用)?
前言
学习React之前来先了解一下js的基础知识!
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是对象?
对象是由属性和方法组成的。面向对象的三大特性是封装,继承,多态
- 属性:是事物的特征,在对象中用属性来表示。
- 方法:是事物的行为,在对象中用方法来表示。
例如:如果值是一个函数,那么我们称之为是对象的方法;
如果值是一个非函数类型,那么我们称之为属性。
1. 对象创建的几种方式
1-1、字面量方式创建
对象字面量是由若干键值对组成的映射表,键值对中间用冒号分隔,键值对之间用逗号分开,整个映射表用花括号括起来。
字面量创建的对象类型为:object、(js中内置的全局的构造函数、Array、String、Number、Function)
const obj = {} //对象都是实例化(类)得来的
console.log(obj);
1-2、早期通过new Object的方式创建
使用new关键字,创建一个Object的实例对象,然后再为其添加属性和方法。
const obj1=new Object()
console.log(obj1);
1-3、工厂模式
//工厂模式
function createStudentObj(name,cls){
return {
name,
cls,
study(){
console.log(`我是${this.name},我能够快乐的学习`);
}
}
}
const zs=createStudentObj('zs',18)
const lisi=createStudentObj('lisi',18)
const wangwu=createStudentObj('wangwu',18)
//问题1:使用工厂模式创建的对象 类型一致 都是Object类型
console.log(zs,lisi,wangwu);
//问题2:相同的逻辑代码(函数)被保存到了不同的内存空间中 导致内存空间的一个浪费
zs.study=123
console.log(lisi.study);
1-4、构造函数
构造函数特点:首字母大写 ,被new关键字调用的
//构造函数的形式
function Person(name,age){
this.name=name
this.age=age
this.eat=function(){
console.log('eat');
}
}
//将eat方法 放置在函数的原型对象
Person.prototype.eat=function(){
console.log('eat');
}
const p1=new Person('zs',18)
const p2=new Person('lisi',18)
const p3=new Person('wangwu',18)
console.log(p1,p2,p3);
二、什么是原型以及原型链
1.原型
原型分为隐式原型和显示原型。
显示原型:每个构造函数身上都有一个prototype属性(显示原型属性),对应的值是原型对象object(是由new object所产生的)。
隐式原型:属性是__proto__,在浏览器显示[[prototype]],每一个实例对象身上都有一个__proto__属性。
原型对象的作用:用来存放一些属性和方法来供实例对象所调用。
2.原型链
当我们访问对象的某个属性时,会先从当前对象中查找,如果没有找到的则继续去对象的proto隐士原型中去查找,如果还没找到则继续向上级对象的原型中查找,直到找到顶层Object对象,如果没有找到返回undefined,这种通过对象的__proto__隐式原型查找属性的链条关系就称之为原型链。
原型链的最顶端是:Object.prototype,它的隐式原型属性值是null。
三、继承的几种方式?
1.原型链继承及它的弊端
// 一个父类对象 (公共属性和方法)
function Person(name, age) {
this.name = "fxh"
this.age = 20
this.friends = []
}
Person.prototype.singing = function () {
console.log(this.name + "--singing--");
}
let p = new Person()
console.log(p);
// 一个子类对象 (特有的属性和方法)
function Student() {
this.sno = 147852
}
Student.prototype = p
let stu = new Student()
console.log(stu); // 不会显示它的属性和方法 , 实际存在
// 1.第一个弊端: 打印stu对象, 继承的属性是看不到的
console.log(stu.name);
//2.第二个弊端: 创建出来两个stu的对象
var stu1 = new Student()
var stu2 = new Student()
// 直接修改对象上的属性, 是给本对象添加了一个新属性
stu1.name = "zzz"
console.log(stu1.name);
console.log(stu2.name);
// 获取引用, 修改引用中的值, 会相互影响!!!!
stu1.friends.push("kobe")
console.log("111",stu1);
console.log("222",stu2);
// 3.第三个弊端: 在前面实现类的过程中都没有传递参数
var stu3 = new Student("lilei", 112)
console.log(stu3);
2.借用构造函数继承
function Person(name,age,friends){
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.singing = function(){
console.log(this.name+"singing");
}
let p = new Person()
function Student(name,age,friends,sno){
Person.apply(this,[name,age,friends])
this.sno = sno
}
Student.prototype = p
var stu = new Student("fxh",20,["xxx"],123456)
console.log(stu);
借用构造函数继承存在的弊端??
无论在什么情况下,都会调用两次父类构造函数,所有的子类实例对象上会拥有两份父类的属性,一份是自己本身的,另一份是在子类对应的原型对象中
3.寄生组合式继承
// 父类 公有属性和方法
function Person(name,age,friends){
this.name = name
this.age = age
this.friends = friends
}
Person.prototype.singing = function(){
console.log(this.name+"singing");
}
// 子类 特有属性和方法
function Student(name,age,friends,sno){
Person.call(this,name,age,friends)
this.sno = sno
}
// 让子类的原型对象 == 新创建的一个对象 并且 新创建的对象的原型是父类的原型
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
var stu = new Student("范筱涵",20,["xxx"],123456)
console.log(stu);
4.class类继承
class Person{
constructor(name,age){
this.name = name
this.age = age
}
eat(){
console.log('这是父类方法')
}
}
class Student{
constructor(name,age,sno){
super(name,age) //super关键字实现继承
}
this.sno = sno
}
四、this指向问题?
-
全局作用域 this 指向 window
-
函数中的 this 指向调用者
-
箭头函数的 this 一般指向定义时所在的作用域
-
事件中 this 指向触发这个事件的对象(dom)
"use strict" // ES6转ES5时 会默认开启严格模式 是undefined
在浏览器中 全局作用域中的this指向的式window
console.log(this);//window
function test() {
console.log(this);
}
test() // 默认绑定 指向window
const obj = {
name: "张三",
fn() {
console.log(this);
}
}
obj.fn() // 隐式绑定
obj.fn.call({}) // 隐式丢失(显示绑定)
test.apply(obj) // 是obj apply call bind 是改变this指向
obj.fn.apply('abc') //this ->abc
五、call()、apply()、bind()的区别?
apply 方法:apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。
call方法:call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
bind方法:bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
-
三者都是用来改变this指向的。
-
三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
-
call和apply在调用时会执行,而bind在调用时会返回一个新的函数,需要进行手动调用。
-
三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
-
bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行 。
六、对作用域链的理解?
-
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到
window
对象即被终止,作用域链向下访问变量是不被允许的 -
简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
七、new操作运算符做了什么(作用)?
-
创建一个空对象,并且this变量引用该对象。
-
该对象的隐式原型属性指向该函数的原型对象,同时还继承了该函数的原型。
-
该对象的属性和方法被加入到this引用的对象中。
-
新创建的对象由this所引用,并且最后隐式的返回this。