JS 疑难解析

this[0] 是什么东西?

在查看jQuery源码时,看到了这样的写法。网上搜索一番后,解释是this绑定了上层的数组,作为下标提取数组元素。

call如何实现继承

本文有借鉴和受原文启发。原文地址:https://blog.51cto.com/u_13496570/2411590
我一直都在好奇为什么call方法可以实现继承;起初我以为是返回值的原因,但是在官方的资料中并没有和这个原因有关;之后我也就是打算硬记,可以就行,但是我始终就是有一个困惑在。后来在网上查询到了一篇文章,不仅解惑,而且还有了关于其他的理解;

call方法是将this值进行修改,还是扩展?

首先明确一点,call是将this进行修改还是扩展;

var obj = {
	name: 'xiaowang',
	age: 18,
	school: "xxx school",
	say: function () {
		console.log("age:" + this.age + ",name:" + this.name + ",school:" + this.school);
	}
}
var newObj = {
	name: 'xiaohong',
	age: 30,
}
obj.say()
obj.say.call(newObj)
/*
age:18,name:xiaowang,school:xxx school
age:30,name:xiaohong,school:undefined
*/

如果call是扩展,明显第二个输出的school应该是xxx school,然而不是,所以这里的call是将this修改;而不是扩展。

function c() {
	this.num = 23;
}

function cpp() {
	c.call(this);
	this.str = "cpp";
}

var obj = new cpp();
console.log(obj);
/*
cpp {num: 23, str: 'cpp'}
*/

new在暗处做了什么?

function c(){
	this.name = "abc";
	this.fn = ()=>{
		console.log("i am fn!");
	}
}
var obj = new c();
console.log(obj);
/*
console的结果:
c {name: 'abc', fn: ƒ}
*/

明显对象obj中有函数c的属性name, fn;
最开始我以为是每个函数c的属性都会被new到对象中;

function c(){
	name = "abc";
	fn = ()=>{
		console.log("i am fn!");
	}
}
var obj = new c();
console.log(obj);
/*
c {}
*/

经过测试,对象是没有之前的情况的,说明new操作符会将函数的this值继承。

call方法会将this进行修改,所以在new时会将call修改的this进行继承。

function c(){
	this.name = "abc";
	fn = ()=>{
		console.log("i am fn!");
	}
}
var obj = new c();
console.log(obj);
/*
c {name: 'abc'}
*/

可以看到,再次修改后确实如此。

call()或apply()

每个函数都包含两个非继承的方法:appyly()和call()

call() || apply()

apply()方法接收两个参数:一个是在其中运行的作用域,另一个是参数数组。

call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call方法时,传递给函数的参数必须逐个列举出来。

function sum(num1, num2) {
	return num1 + num2;
}

function callSum(num1, num2) {
	return sum.call(this, num1, num2);
}

至于是使用appy()还是call(),完全取决于你采取那种给函数传递参数的方式最方便。如果你打算直接传入arguments对象,或者包含函数中先接收到的也是一个数组,那么使用apply()肯定方便;否则,选择call()可能合适;在不给函数传递参数的情况下,是用那个方法都无所谓。

扩充函数运行的作用域

事实上,传递参数并非apply()和call()真正的用武之地。而是扩充,准确来说是修改:

winsow.color = "red";
var o = { color: "blue"};
function sayColor() {
	consolo.log(this.color);}
sayColor(); //red
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call(o);//blue

回调函数的api实现

本文使用js代码作为实例

在经常使用某些api的使用, 经常有将函数作为参数使用的api, 虽然我知晓了回调的一些知识, 但是我一直以来虽然都对那些api的调用都没有进行过好奇的探讨. 就比如说,

  1. 为什么我可以将函数作为参数使用?
  2. 为什么我可以定义的函数的参数有属性和方法?

question_1

这个其实我不是特别的好奇, 因为之前学习回调的时候模拟过:

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(); // 回调函数
}

function test() {
  console.log('as');
}

fn(test);

/*es6匿名函数的简写
fn(()=>{
	console.log('as');
})
/*

当然这个不是我主要好奇的地方, 回调函数如果有参数应该怎么处理

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(pram1, pram2); // 回调函数
}
// 在这里面, 确实可以有参数的传递, 但问题是callback是回调函数, 这里的参数pram1, pram2应该怎么传;
function test(a = 1, b = 2)// 想着以这种方式进行,但是会报错;毕竟这种写法异想天开了
{
	console.log(a,b)
}
fn(test)// 这么写是会报错的; 因为pram1,pram2并没有定义,

之后我就想着,能不能以将参数传递的方式进行赋值,然后传递呢?也就是这样的

function fn(callback, a, b) { // 通过参数a, b来获取回调的参数;
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(a, b);// 回调参数
}

function test(a, b)
{
	console.log(a,b)
}
fn(test, 2, 3)// 参数传递2, 3作为回调函数test的参数值

这个是可以运行的, 不过尝试的途中, 发现了一件有趣的事情, 以前从来不知道的事情, 什么来说, 我都不知道为什么?
也就是这样的写法

function fn(callback, a, b) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(a, b); // 回调函数
}
// 在定义回调函数的时候进行默认参数的处理, 应该是叫默认参数吧!
function test(a = 2, b = 3)
{
	console.log(a,b)
}
fn(test) // 这个竟然能成功运行, 并且有结果显示; 奇了怪了.

我试着将fn(callback, a, b) 删掉, 然后运行报错, 于是我猜测, test函数是作为第一参数进行传递, 而test的参数a, b是作为第二, 三参数传递; 于是我试验了一下!

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(arguments[2], arguments[3]); // 参数数组第三个,参数数组第四个; argument[0]最开始我以为是自己的名字,后来查阅官方文档后不是; 于是我又奇了怪了;为什么这个可以进行正确的输出
}
function test(a = 2, b = 3)
{
	console.log(a,b)
}
fn(test)

于是我又直接先迭代argument对象的里面有什么?

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  for(let i of arguments) {
    console.log(i);  //见鬼了, 为什么又只有一个参数[Function: test]; 那之前的那几个arguments[]又是怎么回事?
  }
}
function test(a = 2, b = 3)
{
	console.log(a,b)
}
fn(test)

于是, 我又猜测, 可能在进行调用回调的时候, arguments对象发生了变化,也就是从fn的arguments对象变成了test的arguments对象. 当然我先是直接添加了一句console.log(arguments[0], arguments[1], arguments[2]);试验了一下, fn的arguments有没有东西, 结果是[Function: test] undefined undefined. 然后我直接干脆在test中添加了第三个参数c来打印一下;

function fn(callback) {
    if(typeof callback !== 'function')  throw 'Callback must be a function.'
    //...
    console.log(arguments[0], arguments[1], arguments[2]);
    callback(arguments[1], arguments[2], arguments[0]);
  }
  function test(a = 2, b = 3, c)
  {
      console.log(a, b, c) //[Function: test] undefined undefined     2 3 [Function: test]
  }// 结果是打印的有arguments[1], arguments[2]参数, 并且有arguments[0]还是和fn一样的[Function: test]
  fn(test)

好,现在假设就是它们的回调的函数的argument对象不一样, 那么我之前的callback(arguments[2], arguments[3]);哪里来的结果, 又是为什么结果还是正常的;

function fn(callback) {
    if(typeof callback !== 'function')  throw 'Callback must be a function.'
    //...
    // console.log(arguments[0], arguments[1], arguments[2]);
    callback(arguments[2], arguments[3], arguments[0]);
  }
  function test(a = 2, b = 3, c)
  {
      for(let i = 0; i < 6; i++) {
          console.log(i, arguments[i]); //出乎我的意外
/*
0 undefined
1 undefined
2 [Function: test]
3 undefined
4 undefined
5 undefined 
*/
      }
      console.log(a, b, c)// 2 3 [Function: test]
  }
  fn(test)

说句实话, 我已经有些懵逼了; 这我先要看一下默认参数的文档去了; 我好想悟了!~
在官方有个传入 undefined vs 其他假值在实例中, 传入undefined和不传入,都会是默认的参数值;
那么在之前, 我打印的fn的参数中, 那些个arguments什么2啊3啊 都是 undefined的, 所以可能的情况时, 根本就不是什么arguments对象的不同;
然后我又试了一下这个:

function fn(callback) {
    if(typeof callback !== 'function')  throw 'Callback must be a function.'
    //...
    callback();//没有进行传入参数, 但是可以正常运行.
  }
  function test(a = 2, b = 3)
  {
      console.log(a, b) //2, 3
  }
  fn(test)

回到之前的例子, 打印出来的fn的arguments对象只有一个; 那么那些arguments的2啊3啊都不undefined的; 所以才会是这样. 再次验证:

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(arguments[0])
}
// 打印出 [Function: test] 3
function test(a = 2, b = 3)
{
	console.log(a,b)
}
fn(test)

浪费一大堆时间在这个傻逼问题上了. 总结一下, 就是一个傻逼盯着个傻逼问题犯了半天的傻逼楞;

  1. 通过这种将回调函数进行一个默认参数值处理来达到回调函数的传参好像是可行的.
  2. 通过fn(callback, a, b) 这种方式传参好像也是可以的.
    简单来说好像就是这两种

默认参数值

function fn(callback) {
    if(typeof callback !== 'function')  throw 'Callback must be a function.'
    //...
    callback();//没有进行传入参数, 但是可以正常运行.
  }
  function test(a = 2, b = 3)
  {
      console.log(a, b) //2, 3
  }
  fn(test)

参数传递

function fn(callback, a, b) { 
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  //...
  callback(a, b);
}
function test(a, b)
{
	console.log(a,b)
}
fn(test, 2, 3)

question_2

在接触一些api的时候, 经常会有回调函数的参数在进行一个属性的调用啊,方法的调用啊! 当然这一看就知道参数是个对象;
有了上面的思考, 很容易就想到这样处理:

function fn(callback) {
  if(typeof callback !== 'function')  throw 'Callback must be a function.'
  var o = {header:3, body:2, footer:-101}
  callback(o)
}
fn(c => {
  console.log(c.body); //2
})

在fn中定义一个对象, 然后将这个对象作为参数传递给回调函数; 回调的函数的参数就会是这个对象, 就可以进行一个使用了;

这个好像也没什么要说的, 反正有了上面的探讨, 当时就很容易想到这样处理;

这个应该就解决了第二个问题; OK 除了上面的arguments对象犯傻之外; 写这篇博客还算心情可以! fh gl

JS的原型链以及作用域链

# 原型链
## 原型
有句话是这么说好像:JS是基于原型的语言。简单理解就是JS的所有对象都是基于原型的。当然有一个例外:object.create(null)
### 空对象的原型

首先我们创建一个空对象var o = {},然后访问这个对象。从控制台可以看到,即使是空对象,也有一个prototype:object属性,很明显这是一个对象属性,我们一般称之为原型对象。如图:

我们注意到在这里又有一个__proto__:object属性,访问这个对象,会看到如图的结果:

我们注意到在这个属性的里面又有一个__proto__属性,而这时的属性值不再是一个对象,而是null,也就是在对象的终点的__proto__是一个null空值。

object.prototype的原型

在空对象的原型中,我们发现,最底层的__proto__属性是一个null。并且,我们注意到:即使是空对象,这里暂时称为第三层的原型对象,也只是第二层,而第一层的原型对象:null。却和空对象有着三层的关系。

那么和第一层有着直接关系的第二层是对象是什么?var o = object.prototype这个便是第二层了。

### 一般对象的原型
我们创建一个var o = object.create({}),也就是创建有4层的对象,但是我们实际观察后,发现又一个东西,需要理解一下——prototype和__proto__的关系?

prototype和__proto__以及constructor的关系?

前面的都是些废话,一个月前写的,理清思路的,屁用没有,当做纪念算了。
这些东西前段时间我理清楚了,但是最近发现自己忘了。
记录一下。

一般的对象有:

  • __proto__对象的__proto__指向原型对象
  • constructor对象的constructor指向构造函数
    注意对象是没有prototype

函数有:

  • prototype指向自己的原型对象
  • __proto__指向自己的函数
  • constructor指向Function的函数
function f() { 
    var i = "i am f";
 }
//prototype 返回对象类型原型的引用
console.log(f.prototype.__proto__ == Object.prototype); //true  \\\typeof Object == function
console.log(f.__proto__ == Function.prototype); //true  \\函数的__proto__不同于对象而是指向于Function的原型。
// 可以理解为函数有两条继承线,一条是自己的,一条是Function的。
console.log(f.constructor == Function);// True  \\\函数的constructor指向Function

var o = new f();
console.log(o.prototype);// undefined \\\对象没有prototype
console.log(o.__proto__ == f.prototype);//true
console.log(o.constructor == f);//true  \\\constructor指向自己的构造函数

作用域链

js typeof返回值

数据的typeof返回值

string

typeof "abc" //'string'

boolean

typeof true //'boolean'

object

typeof {} //'object'

undefined

typeof undefined //'undefined'

null

typeof null //'object'

function

typeof function f(){} //'function'

number

typeof 123 //'number'

symbol

typeof Symbol() // 'symbol'

js内置对象的typeof返回值

大多为function

posted @ 2021-09-17 11:43  no_no  阅读(21)  评论(0)    收藏  举报