1.属性、字面量
字面量(literal)是用于表达一个固定值的表示法,又叫常量
通俗的理解,字面就是所见即所得,js 程序执行到代码中的字面量,会立即知道它是什么类型的数据,值是多少
可以用于表示固定值,比如:数字、字符串、undefined、布尔类型的字面值等
属性如下
const person = {
name: "张三",
age: 19,
sex: "男"
}
2.in、for...in
for...in
var mycars = new Array()
mycars[0] = "Saab"
mycars[1] = "Volvo"
mycars[2] = "BMW"
for (x in mycars)
{
console.log(mycars[x] + "<br />")
}
//主要用来进行枚举(如下,对象的循环)
in
判断对象是否为数组/对象的元素/属性:
格式:(变量 in 对象)......注意,
当“对象”为数组时,“变量”指的是数组的“索引”;当“对象”为对象是,“变量”指的是对象的“属性”。
数组示例
var arr = ["a","b","2","3","str"];
var result = ("b" in arr);
var result1 = (4 in arr);
console.log(result,result1) //true false
对象示例
var obj={
w:"wen",
j:"jian",
b:"bao"
}
var result=(2 in obj);
var result1=("j" in obj);
console.log(result)+"<br/>"; //false
console.log(result1)+"<br/>"; //true
3.深拷贝、浅拷贝
单纯数组与对象的赋值叫做浅拷贝(藕断丝连)
let person = {
name: "张三",
age: 19,
sex: "男"
}
let newPerson = person;
newPerson.name = "李四";
console.log(person);
console.log(newPerson);
let arr =[1,2,3]
let newArr = arr
newArr.push(4)
console.log(arr, newArr)
解构赋值是深拷贝还是浅拷贝(?)'
针对一维数组和对象可以看作是深拷贝,多维的是浅拷贝
let arr =[1,2,3]
let newArr1 = [...arr]
newArr1.push(4)
console.log(arr, newArr1)
let arr2 = [[1,2,3],[4,5,6]]
let newArr2 = [...arr2]
newArr2[0].push(888)
console.log(arr2, newArr2)
深拷贝用法
let list = [ {id:1,name: '小明',class: "五年级二班"}, {id:2,name: '小红',class: "五年级三班"}, {id:3,name: '小兰',class: "五年级四班"}]
let newList = JSON.parse(JSON.stringify(list)); //深拷贝(常用,简单便捷)=> 80%解决
newList.push({id: 888})
console.log(list, newList)
//标准的深拷贝 => 引用数据类型(数组、对象)
function deepClone(source){
// [] => Array(基类) {} => object
const targetObj = source.constructor === Array ? [] : {} //外层
for(let keys in source){
if(source.hasOwnProperty(keys)){
//keys => 3 (基本类型、数组、对象)
if(source[keys] && typeof source[keys] === 'object'){
//引用数据类型(数组、对象)
targetObj[keys] = deepClone(source[keys])
}else {
//基本数据类型,直接赋值
targetObj[keys] = source[keys]
}
}
}
return targetObj
}
4 this 指向问题
console.log(this) // window
function a() {
console.log(this) //window 全局的方法都会被当作属性挂载到window上,所以指向widown
}
window.a()
//谁调用 => 指向上一个调用者
let o = {
name: "张三",
fn: function(){
console.log(this)
}
}
o.fn()
let obj = {
name: "张三",
fn1: function(){
console.log(this.name)
}
}
// window.obj.fn1()
let j = {
a: 10,
b: {
a: 12,
fn: function(){
console.log(this.a)
}
}
}
j.b.fn()
var id = 66;
function fn2(){
//箭头函数没有作用域 没有this 它会指向外部
setTimeout(()=> {
console.log(this.id)
},100)
}
// fn2({id: 22}) //怎么指向id为22 call、apply、bind
fn2.call({id:22})
//call、apply、bind 有什么区别
// call和apply只是传参上有区别 改变this之后还会执行一次函数,
// apply => [] call => {}
//例如:
var obj = {}//定义一个空的对象
function f(x,y){
console.log(x,y)
console.log(this) //this是指obj
}
f.apply(obj,[1,2]) //后面的值需要用[]括起来
f.call(obj,1,2) //直接写
// bind调用只改变this,不会执行函数
构造函数中,this指向构造函数的实例
普通函数
const Animal = function (name, age){
this.name = name;
this.age = age;
}
const animal = new Animal('dog', 3);
console.log(animal); // {name: 'dog', age: 3}
箭头函数
const Animal1 = (name, age) => {
this.name = name;
this.age = age;
}
const animal1 = new Animal1('dog', 3);
console.log(animal1); // Uncaught TypeError: Animal is not a constructor
5.new关键字
function Person (){
this.name = "小明"
this.fn = function(){
console.log(`名字是: ${this.name}`)
}
}
let person1 = new Person(); //继承Person的属性和方法
person1.fn()
// 1.创建一个空对象
let obj = new Object({}) // Object => 基类
// 2.设置它的原型链
obj._proto_ = Person.prototype //被构造obj的实例的_proto_ 指向 Person构造函数的原型
// 3.改变this指向
let result = Person.call(obj)
// 4.判断返回值类型
if(typeof (result) == "object"){
person1 = result
}else {
person1 = obj
}
//构造函数里面 默认 => 新创建的对象
//普通函数里面默认return undefined
// let obj = Object.create(null) //没有原型链
// let obj2 = {} //有原型链
6.Symbol
Symbol是由ES6规范引入的一项新特性,它的功能类似于一种标识唯一性的ID。通常情况下,我们可以通过调用Symbol()函数来创建一个Symbol实例:
let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
typeof s1 // 'symbol'
s1 === s2 // false
s2 === s3 // false
let obj = {
[Symbol('name')]: '李一两',
age: 18,
title: 'Engineer'
}
Object.keys(obj) // ['age', 'title']
for (let p in obj) { console.log(p) // 分别会输出:'age' 和 'title' }
Object.getOwnPropertyNames(obj) // ['age', 'title']
由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外,
使用Object的API Object.getOwnPropertySymbols(obj) // [Symbol(name)]
// 使用新增的反射API Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
可以用来定义常量,以避免重复i
注册全局Symbol
let gs1 = Symbol.for('global_symbol_1') //注册一个全局
Symbol let gs2 = Symbol.for('global_symbol_1') //获取全局Symbol
gs1 === gs2 // true
8.optional chaining '?.'
if (db && db.user && db.user.name) nameLength = db.user.name.length;
const nameLength =
(db
? (db.user
? (db.user.name
? db.user.name.length
: undefined)
: undefined)
: undefined);
你当然不想编写这样的代码,因此希望有其他选择。其他一些语言使用了被称为“optional chaining”
(可选链)的功能提供了一种优雅的解决方案。根据最近的规范,“Optinal Chain 是一个或多个属性访问和函数调用的链,其中第一个以令牌`?.`开头”。
const nameLength = db?.user?.name?.length;
[ https://developer.mozilla.org/zhCN/docs/Web/JavaScript/Reference/Operators/Optional_chaining](url)
9.类型转换Sym.toPrimitive
**`Symbol.toPrimitive`** 是内置的 symbol 属性,其指定了一种接受首选类型并返回对象原始
值的表示的方法。它被所有的[强类型转换制]算法优先调用。
ToPrimitive(input, PreferredType)
input是调用的对象,PreferredType是期望返回的结果类型
// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果。
const obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"
// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果。
const obj2 = {
[Symbol.toPrimitive](hint) {
if (hint === "number") {
return 10;
}
if (hint === "string") {
return "hello";
}
return true;
},
};
console.log(+obj2); // 10 — hint 参数值是 "number"
console.log(`${obj2}`); // "hello" — hint 参数值是 "string"
console.log(obj2 + ""); // "true" — hint 参数值是 "default"
1.
let a = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive]() {
return 2;
}
}
1 + a // => 3
'1' + a // => '12'
垃圾回收机制
- 内存:由可读写单元组成,表示一片可操作空间;
- 管理:人为的去操作一片空间的申请、使用和释放;
- 内存管理:开发者主动申请空间、使用空间、释放空间;
- 管理流程:申请-使用-释放
var a=1 只要用到它就不是垃圾
function fn(){
var b=2
console.log(b)
}
let user = {
name: "John"
};
user=null
性能优化
1.避免使用全局变量
2.减少判断层级
3.减少数据读取次数 对于频繁使用的数据,我们要对数据进行缓存。