js知识补充(个人)

JS知识补充

JS基本介绍

JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。 这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升
JavaScript 是一种动态类型语言,也就是说,变量的类型没有限制,变量可以随时更改类型。

var a = 1;
a = 'hello';

js数据类型

JavaScript 基本数据类型:

  • undefined

  • null

  • string

  • number

  • boolean

      堆和栈都是内存中划分出来用来存储的区域。
      栈(stack)为自动分配的内存空间,它由系统自动释放;
      而堆(heap)则是动态分配的内存,大小不定也不会自动释放。
    

基本数据类型存放在栈,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。
基本数据类型值不可变(不太清楚)
基本数据类型的比较是值得比较

	var a = 1;
    var b = true;
    console.log(a == b);//true

引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况开进行特定的分配,引用类型值可变

var person1 = {name:'jozo'};
var person2 = {name:'xiaom'};
var person3 = {name:'xiaoq'};

引用类型的比较是引用的比较

	var a = [1,2,3];
    var b = [1,2,3];
    console.log(a === b); // false

布尔值转换:
如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。

  • undefined
  • null
  • false
  • 0
  • NaN
  • " "或’ ’

注意,空数组([])和空对象({})对应的布尔值,都是true。

Math
parseInt()
使用内置函数 parseInt() 将字符串转换为整型。如果字符串头部有空格,空格会被自动去除。如果parseInt的参数不是字符串,则会先转为字符串再转换。

parseInt('   81') // 81
parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1

NaN(Not a Number 的缩写)

变量

在 JavaScript 中声明一个新变量的方法是使用关键字 let 、const 和 var:

  1. let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
  2. const 允许声明一个不可变的常量。
  3. var 是最常见的声明变量的关键字。它没有其他两个关键字的种种限制。这是因为它是传统上在 JavaScript 声明变量的唯一方法。使用 var 声明的变量在它所声明的整个函数都是可见的。
    如果声明了一个变量却没有对其赋值,那么这个变量的类型就是 undefined

运算符

自增和自减运算符:++/–
运算之后,变量的值发生变化,这种效应叫做运算的副作用(side effect)。自增和自减运算符是仅有的两个具有副作用的运算符,其他运算符都不会改变变量的值。放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。

var x = 1;
var y = 1;

x++ // 1
++y // 2
  1. +=,-=
x += 5; // 等价于 x = x + 5;

指数运算符:
指数运算符是右结合,而不是左结合。即多个指数运算符连用时,先进行最右边的计算。

// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
  1. 一个字符串加上一个数字(或其他值),那么操作数都会被首先转换为字符串。 加法运算符是在运行时决定,到底是执行相加,还是执行连接。也就是说,运算子的不同,导致了不同的语法行为,这种现象称为“重载”(overload)。由于加法运算符存在重载,可能执行两种运算,使用的时候必须很小心。
"3" + 4 + 5; // 345
3 + 4 + "5"; // 75

技巧——通过与空字符串相加,可以将某个变量快速转换成字符串类型。

  1. 相等的比较稍微复杂一些。由两个“=(等号)”组成的相等运算符有类型自适应的功能
123 == "123" // true
1 == true; // true

如果在比较前不需要自动类型转换,应该使用由三个“=(等号)”组成的相等运算符:

1 === true; //false
123 === "123"; // false

!= 和 !== 两种不等运算符,具体区别与两种相等运算符的区别类似。

取反运算符
如果对一个值连续做两次取反运算,等于将其转为对应的布尔值,与Boolean函数的作用相同。这是一种常用的类型转换的写法。

!!x
// 等同于
Boolean(x)
  1. && 和 || 运算符使用短路逻辑(short-circuit logic),是否会执行第二个语句(操作数)取决于第一个操作数的结果。在需要访问某个对象的属性时,使用这个特性可以事先检测该对象是否为空:
var name = o && o.getName();

或用于缓存值(当错误值无效时):

var name = cachedName || (cachedName = getName());

当作if语句使用

if (i) {
  doSomething();
}

// 等价于

i && doSomething();

只通过第一个表达式的值,控制是否运行第二个表达式的机制,就称为“短路”(short-cut)

var x = 1;
true || (x = 2) // true
x // 1

对象

  1. 创建一个对象原型,Person,和这个原型的实例,You
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 定义一个对象
var You = new Person("You", 24); 
// 我们创建了一个新的 Person,名称是 "You" 
// ("You" 是第一个参数, 24 是第二个参数..)

对象的引用

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量。

var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}

上面代码中,o1和o2指向同一个对象,然后o1的值变为1,这时不会对o2产生影响,o2还是指向原来的那个对象。但是,这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝。

属性的读取
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。

var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2

var obj = {
  123: 'hello world'
};

obj.123 // 报错
obj[123] // "hello world"

深拷贝
在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
默认情况下对象之间的直接赋值都是浅拷贝

数组

  1. Array.length 并不总是等于数组中元素的个数,如下所示:
var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length; // 101
  1. 数组方法
参数描述
index必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany必需。要删除的项目数量。如果设置为 0,则不会删除项目
item可选。向数组添加的新项目。

函数

  1. 函数参数,实际上是访问了函数体中一个名为 arguments 的内部对象,这个对象就如同一个类似于数组的对象一样,包括了所有被传入的参数。下面函数,它可以接收任意个数的参数:
function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum;
}

add(2, 3, 4, 5); // 14

可以使用剩余参数来替换arguments的使用。剩余参数操作符在函数中以:…variable 的形式被使用,它将包含在调用函数时使用的未捕获整个参数列表到这个变量中。

function avg(...args) {
  var sum = 0;
  for (let value of args) {
    sum += value;
  }
  return sum / args.length;
}

avg(2, 3, 4, 5); // 3.5

采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

传值方式
函数参数果是原始类型的(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部

如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

变量提升
函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}

// 等同于
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}

函数本身作用域
函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1

函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

闭包

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在 JavaScript 语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

数据类型转换

JavaScript 是一种动态类型语言,变量没有类型限制,可以随时赋予任意值。

虽然变量的数据类型是不确定的,但是各种运算符对数据类型是有要求的。如果运算符发现,运算子的类型与预期不符,就会自动转换类型。

强制转换
强制转换主要指使用Number()、String()和Boolean()三个函数,手动将各种类型的值,分别转换成数字、字符串或者布尔值。
Number()要比parseInt函数严格,只要有一个字符无法转成数值,整个字符串就会被转为NaN
自动转换
第一种情况,不同类型的数据互相运算。
第二种情况,对非布尔值类型的数据求布尔值
第三种情况,对非数值类型的值使用一元运算符(即+和-)

this关键字

JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,this就是函数运行时所在的对象(环境)。 JavaScript 支持运行环境动态切换,也就是说,this的指向是动态的,没有办法事先确定到底指向哪个对象。

绑定this
call():第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。

func.call(thisValue, arg1, arg2, ...)

apply()与call类似,区别是它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])

小技巧找出数组最大的元素:

var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15

bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

对象的继承

prototype原型对象的作用:
构造函数默认都会有prototype原型对象,构造函数生成的实例可以共享prototype上的属性和方法。

当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。这就是原型对象的特殊之处。
如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。

原型链:就是对象到原型,再到原型的原型……所有对象的原型最终都可以上溯到Object.prototype,也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOf和toString方法的原因,因为这是从Object.prototype继承的。

constructor默认指向prototype对象所在的构造函数,定义在prototype对象上面,可以被所有实例对象继承。constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true。

构造函数的继承
举个栗子,有个父类构造函数Shape:

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

让Rectangle构造函数继承Shape:

function Shape() {
  this.x = 0;
  this.y = 0;
}

Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info('Shape moved.');
};

单个方法的继承,这时可以采用下面的写法。

ClassB.prototype.print = function() {
  ClassA.prototype.print.call(this);
  // some code
}

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值