❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:
文章目录
- 对象中的方法/this
- this使用
- bind函数
- 原型
- 原型链
- 类与实例
- class
- class语法
- 补充
- Map
- 实现Map
- 实现stack
- 实现Queue
- 实现Set
对象中的方法/this
- 面向对象:核心思想就是可以为每种事务(东西),在编程语言中建立一个对应的类型,对应到语言里就是一个class
- 反引号:中间可以插入${}作为动态字符串
- 其实对象中函数的调用就是方法,foo和bar就当作obj对象的方法调用
- 其中foo的this就是指代的obj对象,bar的this指代的也是obj这个对象
<script>
obj = {
foo: function () { },
bar: function () { },
}
let rabbit = {}
rabbit.speak = function (line) {
console.log(`the rabbit says ${line}`)
}
</script>
this使用
- this是函数的一个隐含参数,其值不是传入的,而是取决于该函数的调用方式,不取决于函数在哪定义和在哪调用
- f() = obj.foo
f() // 当函数调用,里面的this指向window
obj.foo() //当obj的方法调用,里面的this指向obj
他们两个的this是不同的 - 这个this指代的是添加的属性对应的对象
<script>
a = [1, 2, 3]
a.push2 = function (val) {
this[this.length] = val
return this.length
}
a.forEach2 = function (action) {
for (var i = 0; i < this.length; i++) {
action(this[i])
}
</script>
-
这个speak是对象属性的简写,speak属性来源于能够读到的一个speak函数的变量名
-
js语言中,函数从来不属于一个对象,this也不会因为这种属于而一成不变
-
在js中,如果两个对象分别有一个属性指向同一个函数,则该函数和这两个对象之间的关系是对等的
<script>
function speak(line) {
console.log(`the ${this.type} rabbit says ${line}`)
}
let whiteRabbit = {
type: "white",
speak
}
let hungryRabbit = {
type : "hungry",
speak
}
speak.call(hungryRabbit,"woshizhu")
</script>
- 在call方法和apply方法中,第一个参数传的就是this,call和apply唯一的区别就是第二个参数apply传的是数组,而call后面传的是一个一个的参数,由此来看apply更好用一些,因为如果数组可以是一个变化的值,而call的话只能是传固定的参数
- 所以,如果一个函数中创建了另一个函数,那么内层函数是拿不到外层函数的this的,除非是外层函数通过call方法,把外层函数的this和内层函数指向同一个函数
- this不能出现在等号的左边,即this本身不能被赋值,this的绑定关系不能被改变。
- 箭头函数是没有this的,在箭头函数里读this,就跟在读一个箭头函数内部没有声明的变量一样。读箭头函数的this,就是读箭头函数外面函数的this
- 由于call/apply并不能改变箭头函数里的this,所以一般都不会使用call和apply来调用箭头函数。apply传不定数量参数的功能由展开运算符。。。来提供。
bind函数
- bind函数的第一个参数是绑定this,如果bind的this已经被绑定了,那么就不能再次更改绑定了
原型
- 从这里我们可以看到,js从一个空对象里取出了一个属性;如果你在一个对象上找不到对应的属性,就会在这个原型对象上找,原型就相当于是一个后备源;所以任何一个对象都有toString的方法,这个方法就是在原型上的
- Object.getPrototypeOf({})这个可以获得对象的原型,object.prototype这个是获取object上的一个属性,这个属性指向原型对象,所以他俩是相等的;数组的原型的原型是对象的原型;最终Object.getPrototypeOf(Object.getPrototypeOf({}))的原型是null
- Object.getPrototypeOf([]) === Array.prototype
- Object.getPrototypeOf(function(){}) === Function.prototype
- Array.prototype和Function.prototype都以Object.prototype为原型
- 这个双中括号phototype就是原型,双中括号代表对象的内部的状态
- 如果想要看到函数的内部状态,用console.dir()
- 还有一点就是不会因为给一个对象的属性赋值而赋值到其原型对象上
原型链
- js就是通过原型链的方式实现了面向对象的
- 原型链在js中其实也只是指针(对象的指向关系 )
- 原型链存在的作用一方面是为了继承,另一方面是为了节省空间;如果一组对象都想有某些方法/属性,则把这些共用的方法/属性放到它们的原型上,可以节省存储空间
类与实例
- 补充一个语法:当在对象的属性中,一个属性指向一个函数(必须是function函数),可以直接将冒号和function删除
- 怎样自己创建原型对象呢?通过Object.create(xxx)
class
- class就是定义了一个类型,构造函数就是一个所有的基础
<script>
var protoRabbit = {
speak(line) {
console.log(`the ${this.type} rabbit says ${line}`)
}
}
function makeRabbit(type) {
let rabbit = Object.create(protoRabbit)
rabbit.type = type
return rabbit
}
var killRabbit = makeRabbit('killer')
var yellowRabbit = makeRabbit('yellowRabbit')
</script>
- 这样写等价于上面那么写,更加方便
- 原型里面就写一些基础的公共方法,然后构造函数就传值,然后用new创建对象
<script>
function Rabbit(type) {
this.type = type
}
Rabbit.prototype = {
speak(line) {
console.log(`the ${this.type} rabbit says ${line}`)
}
}
new Rabbit(yellow);
</script>
- 构造函数的prototype属性 仅仅是作为其构造出来的实例的原型;其实所有的普通函数(非箭头函数)都有prototype属性,但我们一般只关心构造函数的prototype属性
- 除了undifined和null,任何值都有原型,即_ proto 属性, proto _属性就是原型,要区别于原型和构造函数的原型属性
也就是说Object.getPrototypeOf(Rabbit) == Function.prototype
- 上面的那个叫原型属性,下面的那个叫原型,一般如果是构造方法的话,我们就设置上面的那个,也就是设置所谓的原型属性
class语法
- 有一个原型属性的构造函数就可以称为一个类,比上面的写法更简单 ,上面的方法是在2015年之前写的,也就是es6之前
<script>
class Rabbit{
//构造函数
constructor(type){
this.type = type
}
speak(line){
console.log(`ths ${this.type} rabbit says ${line}`)
}
}
let killerRabbit = new Rabbit("killer")
//相当于,class就相当于函数
//let killerRabbit = new class{}
</script>
- 如果把Rabbit typeof一下,那它就是一个function。,上面的speak方法就是放在Rabbit.prototype上面的
补充
- obj自己的x会覆盖原型上的x
- 如果想代替Array.prototype的toString方法,使用object.prototype的toString方法,可以通过Object.prototype.toString.call(ary)来实现,这个输出的是’[object Array]',因此Object.prototype.toString这个函数,给他的this传入什么,它就将该值转换为[object xxx]的字符串,其中xxxx是this缩指向的对象的类型名。所以这个函数可以用来做类型判断,还有typeof也可以做类型判断;但是这个toString无法判断用户自定义的类型(toString.call(rabbit)),这个值是[object,object],本来输出的应该是[object, rabbit];
<script>
function isArray(val){
return Object.prototype.toString.call(val) === '[Object Array]'
</script>
Map
- 通过一个值映射到另一个值,虽然对象上可以实现map的功能,但是如果读出的一个key是不存在的,但是原型上存在,这样就会出现歧义了。一个方法是创建一个没有原型的对象(obj = Object.create(null));
<script>
let ages = new Map()
ages.set("Boris", 39)
ages.set("Liang", 22)
ages.set("Julia", 62) // obj['Julia'] = 62
ages.get('Liang') //obj['Liang']
ages.has('Liang') //'Liang' in obj
</script>
<script>
const val = 'hello world !!'
function lmp(val) {
let map = new Map()
for (let i = 0; i < val.length; i++) {
if (!map.has(val[i])) {
map.set(val[i], 1)
} else {
map.set(val[i], map.get(val[i]) + 1)
}
}
let res = []
let max = 0
map.forEach((value, key) => {
if (value > max) {
res = [key]
max = value
} else if (value == max) {
res.push(key)
}
})
return [max, res]
}
let a = lmp(val)
</script>
- set,get,has是map的其中一些接口,map这些操作的性能都是O(1)的,是非常快的。
- ages.clear():清除所有的映射对;ages.delete(key):删除一组映射对; ages.forEach((val,key) => {xxx}):遍历这个映射,可以类比于数组,传入的值是数组的值和下标;ages.size:这是一个属性,不是一个方法,可以拿到map里有几组;
- Object.keys(obj):这个可以返回一个对象的自有属性有哪些,原型属性是无法返回的, 这些自有属性组成了一个数组返回。
<script>
let obj = {}
obj.foo = 1
obj.bar = 2
function keys(obj) {
var res = []
for (let key in obj) {
if(Object.prototype.hasOwnProperrty.call(obj,key)){
res.push(key)
console.log(key, obj[key])
}
}
return res
}
</script>
- 有一个方法是obj.hasOwnProperty()方法是继承来的,可以判断该对象有没有对应的自有属性;但是in运算符不同,只要obj上有这个属性,不管是不是原型属性都算上
实现Map
<script>
class Map2 {
constructor() {
this.keys = [] // 用来存储每组映射的key
this.vals = []// 用来存储每组映射的value
}
get(key) {
var keyIdx = this.keys.indexOf(key)
if (keyIdx >= 0) {
return this.vals[keyIdx]
}
}
set(key, val) {
var keyIdx = this.keys.indexOf(key)
if (keyIdx >= 0) {
this.vals[keyIdx] = val
} else {
this.keys.push(key)
this.vals.push(val)
}
return this
}
has(key) {
var keyIdx = this.keys.indexOf(key)
if (keyIdx >= 0) {
return true
}
return false
}
delete(key) {
var keyIdx = this.keys.indexOf(key)
if (keyIdx >= 0) {
//splice函数,从keyIdx位置,删除一项
this.keys.splice(keyIdx, 1)
this.vals.splice(keyIdx, 1)
return true
}
return false
}
clear() {
this.keys = []
this.vals = []
}
size() {
return this.keys.length
}
}
</script>
- 我们不希望外界的人设置keys和vals,在构造函数外面写上#xxx,就变成私有类字段,外界就无法访问了
实现stack
<script>
class stack {
//带警号的要在外面先声明
#size = 0
#top = null
// constructor() {
// this.#top = null // 用于存储栈内元素的链表;由于头节点是栈顶,所以是top
// this.#size = 0
// }
push(val) {
let Node = {
val: val,
next: null
}
Node.next = this.#top
this.#top = Node
this.#size++
return this
}
//返回栈顶元素,并将其出栈
pop() {
if (this.#top) {
let res = this.#top.val
this.#top = this.#top.next
this.#size--
return res
}
}
size() {
return this.#size
}
//查看栈顶元素的值,但不出栈
peek() {
if (this.#top) {
return this.#top.val
}
}
}
</script>
实现Queue
实现Set
- 数组是可重复的有序集合,set是无序的不可重复的集合
- let = new Set(),有add方法,has方法,delete方法,clear方法,size方法
————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章