JS基础知识 - 1
js的数据类型
- string
- number
- boolean
- null
- undefined
- bigint
- symbol
- object
js的原型链
原型链涉及的概念非常多,首先就是原型是什么?
const a = {}
当我创建一个空对象a,我调用a.toString()竟然有返回值,这是因为a.__proto__ === Object.prototype,Object.prototype上定义了toString方法,所以a可以使用。我们叫Object.prototype是a的原型
const a = []
在这个例子中a的原型是Array.prototype而Array.prototype的原型是Object.prototype,此时a.toString()会向上查找原型上是否存在toString,一直找到Object.prototype上,这样一个对象指向其原型对象的内部链接,我们称这为原型链。
const x = Object.create(原型)
设置一个对象的原型可以使用Object.create方法。该对象将沿着原型链继承原型对象上的所有属性和方法。
在没有 Class 的情况下实现「继承」。以 a ===> Array.prototype ===> Object.prototype 为例,我们说:
-
a 是 Array 的实例,a 拥有 Array.prototype 里的属性
-
Array 继承了 Object
-
a 是 Object 的间接实例,a 拥有 Object.prototype 里的属性
这样一来,a 就既拥有 Array.prototype 里的属性,又拥有 Object.prototype 里的属性。
这种方式与class相比,它不支持私有属性
this到底是什么?
在JavaScript中,this关键字表示当前函数的执行上下文。它通常用于引用当前函数所在的对象或者作用域。实际上,this的值取决于当前代码所在的执行上下文。如果当前代码在一个函数中执行,则this通常引用该函数所在的对象;如果当前代码在全局上下文中执行,则this引用全局对象(在浏览器中是 window 对象)
函数通常有以下几种调用方式:
fn()
obj.fn()
fn.call()
fn.apply()
我们可以将前两种调用方式改写成.call()形式
fn.call(undefined)
fn.call(obj)
这样this的值就很好确定了
fn.call(undefined) // this = window
fn.call(obj) // this = obj
构造函数的 new
在 JavaScript 中,当你使用 new 关键字创建一个对象时,实际上发生了以下几个步骤:
- 创建临时对象/新对象
- 绑定原型
- 指定 this = 临时对象
- 执行构造函数
- 返回临时对象
立即执行函数
(function () {
console.log(1)
})()
声明一个匿名函数并立即执行这个函数,这种做法就叫立即执行函数
立即执行函数还有以下几种声明方式:
(function(){alert('我是匿名函数')} ()) // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) () // 用括号把函数包起来
!function(){alert('我是匿名函数')}() // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()
var x = function(){return '我是匿名函数'}()
立即执行函数的作用就是创建一个独立的作用域,避免全局作用域中的变量污染和命名冲突
在ES6中我们可以用以下方式创建局部作用域:
{
let a = 1
console.log(a)
}
js中的闭包
闭包是js的一种语法特性,简而言之就是一个函数内部创建一个函数,此函数可以访问外部函数的局部变量和参数。 下面是一个闭包示例:
function out() {
let count = 0;
function add() {
count++;
console.log(count)
}
return add;
}
const fn = out()
fn() // 1
解决了什么问题:
-
避免污染全局环境。(因为用的是局部变量)
-
提供对局部变量的间接访问。(因为只能 count += 1 不能 count -= 1)
-
维持变量,使其不被垃圾回收。
闭包使用不当可能造成内存泄露。
js实现类
方法一: 使用原型
// 定义一个类
function Person(name, age) {
this.name = name;
this.age = age;
}
// 定义一个方法
Person.prototype.say() = function() {
console.log(`hi! my name is ${this.name}`)
}
// 创建实例
let person = new Person('tom', 18)
person.say()
方法二: 使用 class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
say() {
console.log(`hi! my name is ${this.name}`)
}
}
const person = new Person('jack', 19)
person.say()
需要注意的是,在 JavaScript 中类的实现并不是真正的类,而是使用函数和原型链的方式来模拟类的概念。
js实现继承
方法一: 使用原型链
子类的原型对象中设置父类的实例对象,从而继承父类的属性和方法
// 定义一个父类
function Animal(name) {
this.name = name;
}
Animal.prototype.run = function() {
console.log('run')
}
// 定义一个子类
function Dog(name, color) {
Animal.call(this, name); // 调用父类的构造函数
this.color = color;
}
Dog.prototype = new Animal(); // 将子类的原型对象设置为父类的实例对象
Dog.prototype.constructor = Dog // 修正子类的 construuctor
// 定义子类的方法
Dog.prototype.bark = function () {
console.log('叫')
}
// 创建实例
const dog1 = new Dog('tom', 'red')
dog1.run()
dog1.bark()
如果只想要父类的方法不想要属性
var f = function(){ }
f.prototype = Animal.prototype
Dog.prototype = new f()
方法二: 使用class
class Animal{
constructor(legsNumber){
this.legsNumber = legsNumber
}
run(){}
}
class Dog extends Animal{
constructor(name) {
super(4)
this.name = name
}
say(){
console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
}
}