书接上文,开始重学前端(第二篇)
开篇之初我们先抛出几个问题?
回想一下这个这些问题你心中是否已有答案呢?在接下来的内容中,我们逐一共同学习!
灵魂质问?到底什么是js
JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名的,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。
百度是这样说的,这就不是人话,其实本质上js 是啥?
js就是专门编写网页交互行为的语言
那js是由什么组成的呢,简单来说就一句话
ECMAScript标准+ webAPI 那么我们今天要一起学习的就是ECMASciript中的-Object,他实际上是一个es的语言标准
对象其实有两个特点
既然对象是一个具体事物的属性和功能。那么,事物的属性会成为对象的属性, 事物的功能会成为对象的方法
在程序中,都是先用对象封装一个事物的属性和功能。然后,再调用对象 的方法,来执行任务。这就是面向对象,其实在es6出来之前,js总是显得这么合群,其他语言该有的对象的结构,他是一个没沾上,知道es6横空出世,我们才有了类这个概念,面向对象也才算是正式打响!
由于是重学前端,我们就不讲怎么创建对象这种百度搜索能有100页答案的东西了。
我们来探究一下,对象的底层到底是什么? 咱研究底层之前,我们先来看看数组
数组对象的作用是:使用单独的变量名来存储一系列的值。
数组呢,他还可以分类,分为普通数组,二维数组,hash数组,普通数组,和二维数组不在赘述了,讲讲这个hash数组
哈希数组,又称关联数组,顾名思义,他的数组元素是由key:value组成的
var myhash = new Array();
myhash['new'] = 'newval';
myhash['new2'] = 'newval_2';
上述代码就会产生如下结果:就是是哈希数组的数据结构
到这你是不是发现,我们的对象也能这么去赋值,和取值
如下图,我们发现我们用上述方法去给对象赋值和取值,也可以实现
由此得出结论:对象底层就是 hash 数组,只不过他在关联数组上有添加了许多包装属性,和方法,这样的结构就导致了,对象有这很多特性比如 对象具有高度的动态性,JavaScript给使用者在运行时为对象添改状态和行为的能力。(大佬的总结,我照抄)
在我们日常的认知中,对象只是简单的键值对,其实我们深究的时候发现,对象还提供了一些特征来描述我们的对象成员
通常情况下,这些我们是用不上看不见的,我们只需要关心赋值和取值即可,那你说我非要用咋办?
我们可以通过内置的Object.getOwnPropertyDescripte来查看
var actions = {
b: 1,
c: 2
}
var d = Object.getOwnPropertyDescriptor(actions, "b")
console.log(d)
结果如下
如果想要修改我们可以通过内置的Object.defineProperty来修改,这也是vue能实现数据劫持的原理
var value = { a: 1 };
Object.defineProperty(value, "b", { value: 2, writable: false, enumerable: false, configurable: true });
var obja = Object.getOwnPropertyDescriptor(value, "a");
var objb = Object.getOwnPropertyDescriptor(value, "b");
//我们发现赋值为3已经不能改了
value.b = 3;
console.log(value.b, obja, objb); // 2
那他又是怎么实现数据劫持的呢?别急一步步探究
var value = { b: 1 };
var tm = null;
Object.defineProperty(value, "b",
{
get: function () {
console.log('我取值的时候被调用了一次')
return tm
},
set: function (a) {
console.log('我赋值的时候被调用一次')
tm = a
}
});
//赋值
value.b = 3;
//取值
console.log(value.b);
复制代码
我们发现,其实vue的响应式核心就是 Object.defineProperty这个方法能监听到值的改变,然后去通知已经订阅的各个方法,进行相应的操作,来达到改变视图的目的
说起面向对象,继承必不可少,那么什么是继承呢?
用大白话解释:继承就是父对象的成员,子对象无需创建,就可直接使用
那么我们怎么继承呢?
由于在es6出现之前,我们没有类的概念,我们的语言标准,就沿用了祖师爷发明的原型系统,虽然不是正统语言该有的样子,但也独领风骚,什么都长得像java还能叫js吗?
原型就是新对象持有一个放公用属性和方法的的引用的地方,注意并不真的去复制一个原型对象,而是使得新对象持有一个原型的引用,每个构造函数在出生的时候(constructor)都附送一个原型对象(prototype)
上述概念,就回答了什么叫做原型对象。这里又有一个老生常谈的名字,构造函数
构造函数:专门定义一类对象统一结构的特殊函数。
构造函数和原型以及对象之间的关系如下图所示:
我们发现其实他们之间其实就是靠着新对象用双下划线 proto 继承原型对象,构造函数用 prototyoe引用原型对象
function Person(name) {
this.name = name;
}
// 通过构造函数的 Person 的 prototype 属性找到 Person 的原型对象
Person.prototype.eat = function () {
console.log("吃饭");
}
let Person1 = new Person("aaa", 24);
let Person2 = new Person("bbb", 24);
console.log(Person1.eat === Person2.eat)//发现相等
console.log(Person1)
console.log(Person2)
复制代码
在上图中我们发现,他的双下划线proto中还有双下划线proto这就形成了链条状我们把它叫做原型链
理解上上述原理之后,我们就可以轻松发现,如果要实现当下流行的这些 构造函数继承、组合继承、原型继承、寄生式继承、寄生组合式继承 其实不过是改变他的.prototype指向,从而改变双下划线proto的指向而已(我是这样理解的,错误之处大佬指出),当然在es6大行其道的今天,我们一个class和extends 全部干掉了这些花里胡哨!
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(this.name + ' barks.');
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
复制代码
在了解玩这些概念之后,我们回答一个上面的问题,new到底干了一件什么事?
我的理解这个new关键字其实干了四件事,也很好记忆
上文中提到this指向问题在此梳理一下,几种情况
this的绑定规则总共有下面4种。
首先声明:this的确定是在运行时确定也就是调用时,调用位置就是函数在代码中被调用的位置(而不是声明的位置)
其实我的理解是this是在将这个函数压入执行环境栈的时候就已经被确定了,执行的时候只不过从已经确定的this,指向的地方去拿对象替代this(之前这个this的确定时间问题,被一个阿里大佬问到过,当时也是云里雾里,特地去网上查了很多资料也是各种版本,在这里说一下我的理解,不对之处请大佬指出)
默认指向调用这个方法的对象,如果没有对象去调用,那就不用想了,就指向全局,如果是严格模式,那就是undefined
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
//我们发现前面有对象调用
obj.foo(); // 2
复制代码
var obj = {
b: function () {
console.log(this);
}
}
function b(b) {
//发现前面没有调用对象调用,那就指向window
b()
}2
b(obj.b)//window
复制代码
通过call,apply,bind去绑定this,就叫做显示绑定,这些为啥能实现显示绑定,我就不在赘述,有兴趣可以去看我之前临摹的call,apply,bind的源码 代码抗击病毒之-大厂面试必考题总结
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2用call强制绑定了一下
复制代码
function foo(a) {
this.a = a;
}
var bar = new foo(2); //在new的时候this被绑定到了这个新对象
console.log( bar.a ); // 2
ES6新增一种特殊函数类型:箭头函数,箭头函数无法使用上述规则
var foo =()=> {
console.log(this)
}
var a = {
b: foo
}
//这时发现this无法被改变了
a.b()//window
这种绑定,其实在声明的时候就已经被确定为当前上下文的作用域环境中的this(当时还是被这个阿里大佬问过,箭头函数的this在什么时候确定的,我的理解也是在压入环境栈的时时候,去确定作用域,然后在执行的时候从作用域环境中去拿到this,也就是声明的时候就被绑定,不对指出求指正)
我们都知道万物皆对象,但是其实在js中对象也分种类的,除了我们平常知道的普通对象之外我们还有宿主对象、内置对象接下来一一讲解
由JavaScript宿主环境提供的对象,它们的行为完全由宿主环境决定。
宿主对象就是我们的js运行在的地方他提供的对象,我们最熟悉不过的就是浏览器环境了, 我们的宿主对象就是window,这个window包含的内容千奇百怪一部分来自 JavaScript语言,一部分来自浏览器环境。
内置对象又包含固有对象、原生对象
固有对象是由标准规定,随着JavaScript运行时创建而自动创建的对象实例。
固有对象在任何JS代码执行前就已经被创建出来了,它们通常扮演者类似基础库的角色。我们常用的一些js方法其实就是固有对象
可以由用户通过Array、RegExp等内置构造器或者特殊语法创建的对 象。
我们把JavaScript中,能够通过语言本身的构造器创建的对象称作原生对象。在JavaScript标准中,提供了30 多个构造器,通过这些构造器,我们可以用new运算创建新的对象,所以我们把这些对象称作原生对象。
到这里,我们发现函数其实也是个对象,这就回答了,开头中提到的问题,下面我们来验证一下
function foo() {
}
foo.b = 3
console.log(foo)
console.log(foo.b)//3
复制代码
我们发现,我竟然能给函数赋值一个属性,这是只有对象才能有的特权啊,所以由此说明他也是一个对象
系统的学习了一下对象到底是个什么玩意,并回答了上面的几个问。再次感谢极客大佬的重学前端,让我重新认识js,记录学习,不对之处,欢迎大佬指正!
最后,我们留下一个大佬的查询固有对象的代码 他列举了所有含有固有对象的js对象 三个值:
Infinity、NaN、undefined。
九个函数:
** 一些构造器:**
Array、Date、RegExp、Promise、Proxy、Map、WeakMap、Set、WeapSet、Function、Boolean、 String、Number、Symbol、Object、Error、EvalError、RangeError、ReferenceError、SyntaxError
TypeError
URIError、ArrayBuffer、SharedArrayBuffer、DataView、Typed Array、Float32Array、Float64Array、 Int8Array、Int16Array、Int32Array、UInt8Array、UInt16Array、UInt32Array、UInt8ClampedArray。
四个用于当作命名空间的对象:
Atomics JSON Math Reflect
var set = new Set();
var objects = [
eval,
isFinite,
isNaN,
parseFloat,
parseInt,
decodeURI,
decodeURIComponent,
encodeURI,
encodeURIComponent,
Array,
Date,
RegExp,
Promise,
Proxy,
Map,
WeakMap,
Set,
WeakSet,
Function,
Boolean,
String,
Number,
Symbol,
Object,
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError,
ArrayBuffer,
SharedArrayBuffer,
DataView,
Float32Array,
Float64Array,
Int8Array,
Int16Array,
Int32Array,
Uint8Array,
Uint16Array,
Uint32Array,
Uint8ClampedArray,
Atomics,
JSON,
Math,
Reflect];
objects.forEach(o => set.add(o));
for (var i = 0; i < objects.length; i++) {
var o = objects[i]
for (var p of Object.getOwnPropertyNames(o)) {
var d = Object.getOwnPropertyDescriptor(o, p)
if ((d.value !== null && typeof d.value === "object") || (typeof d.value === "function"))
if (!set.has(d.value))
set.add(d.value), objects.push(d.value);
if (d.get)
if (!set.has(d.get))
set.add(d.get), objects.push(d.get);
if (d.set)
if (!set.has(d.set))
set.add(d.set), objects.push(d.set);
}
}
console.log(set)//441种
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有