前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS高级原型以及函数调用方式

JS高级原型以及函数调用方式

作者头像
用户6256742
发布2024-06-13 13:47:41
750
发布2024-06-13 13:47:41
举报
文章被收录于专栏:网络日志网络日志

JavaScript 高级

目标

  1. 原型
  2. ==函数的原型链==
  3. 函数和对象的原型链关系
  4. 函数的4种调用方式
  5. 箭头函数

2.原型 prototype -重点

原型上存放函数

  1. 解决了同一个 say 浪费 内存的问题
  2. 解决了污染全局变量的问题
代码语言:javascript
复制
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
    }
    // 将刚才的全局函数say 直接挂载到 构造函数的原型上 即可
    // prototype 是个对象 每一个构造函数都会内置有的. 我们称之为原型
    createStudent.prototype.say = function () {
      console.log(this.name);
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // true 

原型解释

  • 原型的单词是 prototype, 原型的这个名字是行业内共同认可的名字。
  • 原型本质是一个对象,理解为 JavaScript 自动帮我们添加的,只要是构造函数,系统会默认的为构造函数关联一个对象,这个对象就称为构造函数的原型,写在原型中的成员,可以被构造函数所创建的实例调用
  • 原型是 JavaScript 自动帮我们在定义构造函数的时候添加的
  • 所有构造函数的实例,共享一个原型
  • 原型上一般是挂载函数

图示

JS高级原型以及函数调用方式
JS高级原型以及函数调用方式

3.原型 proto

Javascript 规定,每一个(构造)函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上,以便让同一类型的对象共享方法或其它成员

实例的 __proto__ 属性 等于 构造函数的 prototype

代码语言:javascript
复制
function Person (name, age) {
  this.name = name
  this.age = age
}

console.log(Person.prototype)

Person.prototype.type = 'human'

Person.prototype.sayName = function () {
  console.log(this.name)
}

var p1 = new Person(...)
var p2 = new Person(...)

console.log(p1.sayName === p2.sayName) // => true
  • 这时所有实例的 type 属性和 sayName() 方法,其实都是同一个内存地址

注意:由于不同浏览器的兼容性问题,我们使用的时候,都只会使用 构造函数的prototype

实例的 _proto_ 只是为了方便我们开发的时候查看数据,是不会手动修改和操作它的。

4.原型的关系

构造函数、实例、原型三者之间的关系

构造函数:构造函数就是一个函数,配合new可以新建对象。

实例:通过构造函数实例化出来的对象我们把它叫做构造函数的实例。一个构造函数可以有很多实例。

原型:每一个构造函数都有一个属性prototype,函数的prototype属性值就是原型。通过构造函数创建出来的实例能够直接使用原型上的属性和方法。

JS高级原型以及函数调用方式
JS高级原型以及函数调用方式

所有的构造函数都是Function的实例

Array 和 Person 和 Date 等都是 Function的实例

JS高级原型以及函数调用方式
JS高级原型以及函数调用方式

Function 和 Object的关系

有人说 JavaScript 是作者花了7天时间写出来的产物 - 不完美

代码语言:javascript
复制
console.log(Object.prototype===Function.prototype.__proto__)
JS高级原型以及函数调用方式
JS高级原型以及函数调用方式

Object的顶端呢?

接近顶峰了

代码语言:javascript
复制
 console.log(Object.prototype.__proto__ === null);
JS高级原型以及函数调用方式
JS高级原型以及函数调用方式

5.原型链

1概念

任何一个对象,都有原型对象,原型对象本身又是一个对象,所以原型对象也有自己的原型对象,这样一环扣一环就形成了一个链式结构,我们把这个链式结构称为:原型链。

JS高级原型以及函数调用方式
JS高级原型以及函数调用方式
  • 总结:Object.prototype是原型链的尽头,Object.prototype的原型是null。

属性查找原则

如果是获取操作

  1. 会先在自身上查找,如果没有
  2. 则根据__proto__对应的原型去找,如果没有
  3. 一直找到Object.prototype,如果没有,那就找不到从而报错

6.es5 原型链继承

利用代码的能力实现 面向对象的特性 封装继承

初体验

  1. 子类strudent 继承了父类 Person的属性
代码语言:javascript
复制
// 父类
function Person(name, height) {
    this.name = name;
    this.height = height;
}

Person.prototype.say = function () {
    console.log(this.name);
    console.log(this.height);
}

// 子类
function Student(grade, name, height) {
    // 借用了父类的构造函数,完成对自己的赋值
    Person.call(this, name, height)
    this.grade = grade;
}

// 赋值了父类原型上的所有的 属性和方法
Student.prototype = Person.prototype;
// 修改之类的指向
Student.prototype.constructor = Student;

// 创建子类的实例
const stu = new Student("一年", "周星星", 170);
stu.say();

7.作用域及作用域链

let所创建的变量的作用域是从创建这个let变量到它所在的结构的}结束

作用域:变量起作用的区域,也就是说:变量定义后,可以在哪个范围内使用该变量。

代码语言:javascript
复制
var num = 11;//全局变量
function fn(){
  var num1 = 22;//局部变量
  console.log(num);  // 全局变量在任何地方都能访问到
  console.log(num1);  
}
console.log(num);

在js里只有全局作用域和函数作用域。

函数作用域是在函数定义的时候作用域就确定下来了,和函数在哪调用无关。

  • 我们只关注函数的定义位置而不关注函数的调用位置
    • 定义函数,函数的作用域就确定了
    • 以后函数做为参数,不影响函数的作用域
代码语言:javascript
复制
var num = 123;
function f1() {
  console.log(num); // 123
}

function f2(){
  var num = 456;
  f1();
}
f2();// 123

作用域链

作用域链:只要是函数,就会形成一个作用域,如果这个函数被嵌套在其他函数中,那么外部函数也有自己的作用域,这个一直往上到全局环境,就形成了一个作用域链。

变量的搜索原则

  1. 从当前作用域开始查找是否声明了该变量,如果存在,那么就直接返回这个变量的值。
  2. 如果不存在,就会往上一层作用域查询,如果存在,就返回。
  3. 如果不存在,一直查询到全局作用域,如果存在,就返回。如果在全局中也没有找到该变量会报错

作用域链练习

代码语言:javascript
复制
// 1 
var num = 10;
fn1();
function fn1() {
  console.log(num);  // undefined
  var num = 20;
  console.log(num);  // 20
}
console.log(num);    // 10


// 2 -- 改造上面的面试题
var num = 10;
fn1();
function fn1() {
  console.log(num);  // 10
  num = 20;
  console.log(num);  // 20
}
console.log(num);    // 20


// 3
var num = 123
function f1(num) {
    console.log(num) // 456
}

function f2() {
    var num = 456
    f1(num)
}
f2()


// 4
var num1 = 10;
var num2 = 20;
function fn(num1) {
  num1 = 100;
  num2 = 200;
  num3 = 300;
  console.log(num1);  // 100
  console.log(num2);  // 200
  console.log(num3);  // 300
  var num3;
}
fn();
console.log(num1); // 10
console.log(num2); // 200
console.log(num3); // 报错

8.this与函数的四种调用模式

根据函数内部this的指向不同,可以将函数的调用模式分成4种

  1. 函数调用模式
  2. 方法调用模式
  3. 构造函数调用模式
  4. 上下文调用模式(借用方法模式)

函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window

代码语言:javascript
复制
function fn(){
  console.log(this);// 指向window 
}
fn();

方法调用模式

当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象

代码语言:javascript
复制
const obj = {
  sayHi:function(){
    console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
  }
}
obj.sayHi();

构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。

代码语言:javascript
复制
function Person(){
  console.log(this);
}
Person();// window
var p = new Person();// person{}

几个小练习

代码语言:javascript
复制
// 分析思路:1. 看this是哪个函数的  2. 看这个函数是怎么调用的,处于什么调用模式

// 1
var age = 38;
var obj = {
    age: 18,
    getAge: function () {
        console.log(this.age);
    }
}

var f = obj.getAge;
f();// 38


// 2
var age = 38;
var obj = {
  age:18,
  getAge:function () {
    console.log(this.age);// 18
    function foo(){
      console.log(this.age);// 38
    }
    foo();
  }
}
obj.getAge();


// 3
var length = 10

function fn() {
    console.log(this.length) // 10
}
var obj = {
    length: 5,
    method: function (fn) {
        fn() 
        arguments[0](); // 3
    }
}
obj.method(fn, 10, 5);

方法借用模式

也叫上下文模式,分为 apply 与 call,bind

call

call方法可以调用一个函数,并且可以指定这个函数的this指向

代码语言:javascript
复制
const RichWumon = {
    name: "富婆",
    say: function () {
        console.log(this.name, " 我要重金求子");
    }
}

const obj = {
    name: "屌丝"
}

RichWumon.say();			// 富婆
RichWumon.say.call(obj);	// 屌丝
call应用
  1. 将伪数组转成数组
代码语言:javascript
复制
let divs = document.querySelectorAll('div'); // 伪数组
// let divs = document.body.children;
console.log(divs);

function change(nodelist) {
    console.log(Object.prototype.toString.call(nodelist));
    return Array.prototype.slice.call(nodelist);

}
apply

就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表

可以利用apply 将 刚才的call 的代码修改一下

代码语言:javascript
复制
const RichWumon = {
    name: "富婆",
    say: function () {
        console.log(this.name, " 我要重金求子");
    }
}

const obj = {
    name: "屌丝"
}

RichWumon.say();			// 富婆
RichWumon.say.apply(obj);	// 屌丝
apply应用

1.简化log方法

代码语言:javascript
复制
// 简化log方法
function log() {
    // 不需要改变this
    console.log.apply(console, arguments);
}
bind方法

**bind()**方法创建一个新的函数, 可以绑定新的函数的this指向

代码语言:javascript
复制
var name = '张三';
function Fn(){
    this.age = 1;
    
    console.log(this.name + this.age);
}

Fn();			// 张三 1

// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
let obj = {
    name:'小强',
}
const newFn = Fn.bind(obj);
newFn();		// 小强 1

this的指向

单独使用,this 指向全局对象

代码语言:javascript
复制
console.log(this); 

函数中的 this 指向全局对象

代码语言:javascript
复制
function show(){
    console.log(this); 
}

show();

在函数内部,this 的指向在函数定义的时候是不能确定的,只有函数执行的时候才能确定

代码语言:javascript
复制
const a = 18;
const obj = {
    a: 19,
    b: {
        a: 20,
        c: function () {
            console.log(this.a); 	// 20
        }
    }
}
obj.b.c();

在方法中,this 指代该调用方法的对象

代码语言:javascript
复制
const obj ={
	name:"小白",
	say:function(){
		console.log(this);		
	}
}
obj.say()

9.箭头函数

格式--定义语法

代码语言:javascript
复制
// 箭头函数是匿名函数,一般做为参数传递
// let test = function (a,b){
//     let sum = a + b 
//     return sum
// }
// let test = (参数) => {函数体}
// 几个小细节
// 1.如果函数体只有一句,那么可以省略{},同时默认会返回函数体的结果,不能写return
// 2.如果只有一个参数,那么可以省略()
// 3.如果没有参数,()也不能省略
// let test = (a,b) =>  a + b 
let test = a =>  a + 10 

let res = test(100)
console.log(res)

特性

代码语言:javascript
复制
// 箭头函数的this是确定的,况且永远不变
// 箭头函数中的this指向  创建这个箭头函数所在对象  的上下文
let obj = {
    name: 'jack',
    say: function () {
        return () => {
            console.log(this) // obj
        }
    }
}
let fn = obj.say()
fn() // obj

let newobj = {}
newobj.fun = fn
newobj.fun() // obj

let rose = {
    name: 'rose'
}

fn.call(rose) // obj
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaScript 高级
    • 目标
      • 2.原型 prototype -重点
        • 原型上存放函数
        • 原型解释
      • 3.原型 proto
        • 4.原型的关系
          • 构造函数、实例、原型三者之间的关系
          • 所有的构造函数都是Function的实例
          • Function 和 Object的关系
          • Object的顶端呢?
        • 5.原型链
          • 1概念
          • 属性查找原则
        • 6.es5 原型链继承
          • 初体验
        • 7.作用域及作用域链
          • 作用域链
          • 作用域链练习
        • 8.this与函数的四种调用模式
          • 函数调用模式
          • 方法调用模式
          • 构造函数调用模式
          • 几个小练习
          • 方法借用模式
          • this的指向
        • 9.箭头函数
          • 格式--定义语法
          • 特性
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档