Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 在线免费视频国产,国产成人精品综合在线,国产另类ts人妖一区二区

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          熬夜7天,我總結(jié)了JavaScript與ES的25個(gè)

          熬夜7天,我總結(jié)了JavaScript與ES的25個(gè)知識(shí)點(diǎn)


          說起JavaScript,大家都知道是一門腳本語言。那么ES是什么鬼呢?ES全稱ECMAScript ,是JavaScript語言的國(guó)際標(biāo)準(zhǔn)。

          最近,我總結(jié)了25條JavaScript的基礎(chǔ)特性相關(guān)的知識(shí)點(diǎn),大家一起看一下吧

          1.嚴(yán)格模式

          • 使用嚴(yán)格模式,可以在函數(shù)內(nèi)部進(jìn)行較為嚴(yán)格的全局和局部的錯(cuò)誤條件檢查
          • 嚴(yán)格模式的編譯指示,"use strict"
          • 創(chuàng)建全局變量,未聲明變量,非嚴(yán)格模式下為創(chuàng)建全局變量;嚴(yán)格模式下為拋出ReferenceError
          • 對(duì)變量調(diào)用delete操作符,刪除變量,非嚴(yán)格模式下為靜默失敗;嚴(yán)格模式下為拋出ReferenceError
          • 操作對(duì)象情況下:a,只讀屬性賦值會(huì)拋出TypeError;b,對(duì)不可配置的屬性使用delete操作符會(huì)拋出TypeError;c,為不可擴(kuò)展的對(duì)象添加屬性會(huì)拋出TypeError。
          • 重名屬性情況:a,非嚴(yán)格模式下沒有錯(cuò)誤,以第二個(gè)屬性為準(zhǔn);b,嚴(yán)格模式下會(huì)拋出語法錯(cuò)誤。
          • 函數(shù)參數(shù)必須唯一,重名參數(shù),在非嚴(yán)格模式下沒有錯(cuò)誤,只能訪問第二個(gè)參數(shù);嚴(yán)格模式下,會(huì)拋出錯(cuò)誤。
          function funValue(value) {    value="dada";    alert(value); // dada    alert(argument[0]); // 非嚴(yán)格模式:dada    // 嚴(yán)格模式模式 dadaqianduan}?funValue('dadaqianduan');
          • 訪問arguments.callee和arguments.caller,在非嚴(yán)格模式下沒有問題,嚴(yán)格模式下拋出TypeError。

          2.Class基礎(chǔ)語法


          在JavaScript當(dāng)中如何聲明一個(gè)類?如何定義類中的方法?如何實(shí)例化對(duì)象?


          我們來看看下面的代碼示例:


          // es5?let dada=function(type) {	this.type=type}?dada.prototype.study=function() {	console.log('魔王哪吒');}?let da1=new dada('程序員')let da2=new dada('It')?da1.constructor.prototype.study=function() {	console.log('dadaqianduan');}da1.study()

          JavaScript constructor 屬性

          定義和用法

          constructor 屬性返回對(duì)創(chuàng)建此對(duì)象的數(shù)組函數(shù)的引用。

          語法

          object.constructor

          constructor 是一種用于創(chuàng)建和初始化class創(chuàng)建的對(duì)象的特殊方法。

          // es6class Da {  constructor(name) { // 構(gòu)造函數(shù)內(nèi)寫屬性    this.name=name;  }  eat() { // 構(gòu)造函數(shù)外寫方法  	console.log('i eat')  }}?const da1=new Da('da1');?console.log(da1.name); // da1console.log(da1);
          1. 一個(gè)類中只能有一個(gè)名為“constructor"的方法,出現(xiàn)多次構(gòu)造函數(shù)constructor方法會(huì)拋出一個(gè)SyntaxError錯(cuò)誤
          2. 在一個(gè)構(gòu)造方法中可以使用super來調(diào)用一個(gè)父類的構(gòu)造方法
          3. 如果沒有指定一個(gè)構(gòu)造函數(shù)方法constructor方法,就會(huì)使用一個(gè)默認(rèn)的構(gòu)造函數(shù)

          3.類的屬性Setter和Getter

          var daObj={ get val() {  return ; }, set val(value) { }}

          get:

          var da={    a: 1,    get val(){        return this.a + 1;    }}?console.log(da.val);//2da.val=100;console.log(da.val);//2?class Da { constructor(type) {  this.type=type } get age() {  return 1 } set age(val) {  this.realAge=val } eat() {  console.log('i am eat') }}let da1=new Da('da1')console.log(da1.age)da1.age=1console.log(da1.realAge)
          class Da { constructor(type, age) {  this.type=type  this.age1=age } get age() {  return this._age } set age(val) {  this._age=val }}

          利用set/get實(shí)現(xiàn)對(duì)element.innerHTML封裝

          class myHTMLElement { constructor(element) {  this.element=element } get html() {  return this.element.innerHTML } set html(value) {  this.element.innerHTML=value }}

          設(shè)置一個(gè)閉包,通過一定的規(guī)則來限制對(duì)它的修改:

          let myName='dada'class Da { constructor(type) {  this.type=type } get name() {  return myName } set name(val) {  myName=val }}

          4.靜態(tài)方法

          在es5中實(shí)現(xiàn)的靜態(tài)方法:

          let Da=function (type) { this.type=type this.eat=function() {  console.log('i eat') }}Da.study=function(book) { console.log('i book');}
          let Da=function(type) { this.type=type}Da.prototype.eat=function() { Da.walk() console.log('i am')}Da.walk=function(){ console.log('walk')}let da1=new Da('da1')da1.eat()?// walk// i am

          靜態(tài)方法在你的實(shí)例化對(duì)象是找不到的

          在es6中的靜態(tài)方法,標(biāo)記static

          class Da { constructor(type) {  this.type=type } eat() {  console.log('i eat') } static study() {  console.log('i study') }}

          5.如何繼承一個(gè)類

          在es5中的繼承:

          // 定義一個(gè)父類let Da=function(type) { this.type=type}// 定義方法Da.prototype.eat=function() { console.log('i am')}// 定義靜態(tài)方法Da.study=function(book) { console.log('i study')}// 定義子類let Da1=function() { // 初始化父類 Da.call(this, 'da1'); this.run=function() {  console.log('i run') }}// 繼承Da1.prototype=Da.prototype

          在es6中的繼承

          class Da { constructor(type) {  this.type=type } eat() {  // Da.walk();  console.log('i eat') } static walk(){  console.log('i walk') }}?class da extends Da { // 構(gòu)造函數(shù) //constructor (type) {  //super(type) //} run() {  console.log('i run') }}let da1=new da('da1')

          6.面向?qū)ο缶幊藽lass

          類的聲明,屬性,方法,靜態(tài)方法,繼承,多態(tài),私有屬性

          // 類的聲明let Da=function(type) { this.type=type this.eat=function() {  console.log('i eat'); }}?let da=new Da('da');
          // prototypelet Da=function(type) { this.type=type}Da.prototype.eat=function() { console.log('i eat')}let da1=new Da('da1')

          es6中的Class

          class Da { // 構(gòu)造函數(shù) constructor(type) {  this.type=type } // 方法 walk() {  console.log('i walk') }}let da=new Da('da');// console.log(typeof Da); function

          7.函數(shù)參數(shù)的默認(rèn)值

          函數(shù)參數(shù)是從左到右解析,如果沒有默認(rèn)值會(huì)被解析成 undefined

          // 參數(shù)默認(rèn)值function da (x,y,z) {}function sum() { let num=0 Array.prototype.forEach.call(arguments, function(item){  num +=item * 1 }) Array.from(arguments).forEach(function(item){  num +=item * 1 }) return num}
          // 不確定function sum(...nums) { let num=0 nums.forEach(function(item){  num +=item * 1 }) return num}console.log(sum(1,2,3,4,5))
          function sum () {  let num=0  Array.prototype.forEach.call(arguments, function (item) {    num +=item * 1  })  return num}?function sum (...nums) {  let num=0  nums.forEach(function (item) {    num +=item * 1  })  return num}

          8.es6箭頭函數(shù)

          箭頭函數(shù)表達(dá)式的語法比函數(shù)表達(dá)式更簡(jiǎn)潔,并且沒有自己的this,arguments,super或new.target。箭頭函數(shù)表達(dá)式更適用于那些本來需要匿名函數(shù)的地方,并且它不能用作構(gòu)造函數(shù)。

          ()=> {}// function Da() {}// let da=function() {}let da=()=> { console.log('hello')}da()?let da=name=> {}
          const materials=[  'Hydrogen',  'Helium',  'Lithium',  'Beryllium'];console.log(materials.map(material=> material.length));// expected output: Array [8, 6, 7, 9]

          拓展

          判斷函數(shù)有幾個(gè)參數(shù)

          1. 在 ES5 中可以在函數(shù)體內(nèi)使用 arguments 來判斷。
          2. 在 ES6 中可以借助 Function.length 來判斷。(統(tǒng)計(jì)第一個(gè)默認(rèn)參數(shù)前面的變量數(shù))
          console.log(sum(...[4]))console.log(sum.apply(null, [4]))

          9.JavaScript中的三個(gè)點(diǎn)(…)

          JavaScript當(dāng)中,函數(shù)的參數(shù)前面有三個(gè)點(diǎn),代表什么呢?我們看下代碼示例:

          function myFunc(a, b, ...args) { console.log(a); // 22 console.log(b); // 98 console.log(args); // [43, 3, 26]};myFunc(22, 98, 43, 3, 26);
          function myFunc(x, y, ...params) { // used rest operator here  console.log(x);  console.log(y);  console.log(params);}?var inputs=["a", "b", "c", "d", "e", "f"];myFunc(...inputs); // used spread operator here// "a"// "b"// ["c", "d", "e", "f"]
          var obj1={ foo: 'bar', x: 42 };var obj2={ foo: 'baz', y: 13 };?var clonedObj={ ...obj1 };// Object { foo: "bar", x: 42 }?var mergedObj={ ...obj1, ...obj2 };// Object { foo: "baz", x: 42, y: 13 }

          10.Object Property

          JS中對(duì)象的屬性定義,代碼示例如下:

          let x='da1';let y='da2';let obj={ x, y}console.log(obj);?// 結(jié)果{x:'da1',y:'da2'}
          let x=1; let y=2; let z=3let obj={ 'x': x, y, [z+y]: 4, * hello() { // 異步  console.log('dada') }}// function* functionName() {}obj.hello()

          其中,*hello是Generator函數(shù),這是ES6提供的一種異步解決方案。

          11.Set數(shù)據(jù)結(jié)構(gòu)

          Set存儲(chǔ)的成員不允許的重復(fù)的(它類似于數(shù)組)

          Set 本身是一個(gè)構(gòu)造函數(shù),用來生成 Set 數(shù)據(jù)結(jié)構(gòu)。

          const s=new Set();?[2, 3, 5].forEach(x=> s.add(x));?Set 函數(shù)可以接受一個(gè)數(shù)組(或類似數(shù)組的對(duì)象)作為參數(shù),用來初始化?const set=new Set([1, 2, 3, 4, 4]);

          實(shí)現(xiàn)數(shù)組去重

          var arr=[1,1,2,2,3,3]; // step1:數(shù)組轉(zhuǎn)集合var s=new Set(arr); // 已經(jīng)去掉重復(fù)值,當(dāng)前不是數(shù)組,而集合s.size; // 3// step2:集合轉(zhuǎn)數(shù)組console.log([...s]); // 1,2,3;?// Array.form 方法可以將 Set 結(jié)構(gòu)轉(zhuǎn)為數(shù)組const items=new Set([1, 2, 3]);const arr=Array.from(items);?function dada(array) {  return Array.from(new Set(array));}?dada([1, 1, 2])

          Set的遍歷

          • keys():返回鍵名的遍歷器
          • values():返回鍵值的遍歷器
          • entries():返回鍵值對(duì)的遍歷器
          • forEach():使用回調(diào)函數(shù)遍歷每個(gè)成員

          操作方法

          • add(value):添加某個(gè)值,返回Set結(jié)構(gòu)本身。
          • delete(value):刪除某個(gè)值,返回一個(gè)布爾值,表示刪除是否成功。
          • has(value):返回一個(gè)布爾值,表示該值是否為Set的成員。
          • clear():清除所有成員,沒有返回值。
          let set=new Set([1, 2, 3, 4, 4]);?// 添加數(shù)據(jù) let addSet=set.add(5);console.log(addSet); // Set(5) {1, 2, 3, 4, 5}?// 刪除數(shù)據(jù) let delSet=set.delete(4);console.log(delSet); // true?// 查看是否存在數(shù)據(jù) 4let hasSet=set.has(4);console.log(hasSet); // false?// 清除所有數(shù)據(jù)set.clear();console.log(set); // Set(0) {}

          實(shí)現(xiàn)并集(Union)、交集(Intersect)和差集(Difference)

          let a=new Set([1, 2, 3]);let b=new Set([4, 3, 2, 1]);?// 并集let union=new Set([...a, ...b]);// Set {1, 2, 3, 4}?// 交集let intersect=new Set([...a].filter(x=> b.has(x)));// set {1, 2, 3}?// 差集let difference=new Set([...b].filter(x=> !a.has(x)));// Set {4}

          12.Map數(shù)據(jù)結(jié)構(gòu)

          JS當(dāng)中的哈希表,使用方法如下:

          let map=new Map()map.set(1, 2)map.set(3, 4)map.set(1, 3)console.log(map)?創(chuàng)建var da=new Map();var jeskson={};遍歷da.forEach(function(value,key,map){}長(zhǎng)度da.size刪除//da.delete() 刪除key,全部清楚da.clear()新增da.set(key,value)da.has(查索引值)?da.forEach((value,key)=>{})?for( let [key, value] of map){}?// let map=new Map( [[1,2], [3,4]] )?map的key任意都可以let o=function() { console.log('o')}map.set(o, 3)console.log(map.get(o)); // 3
          // map.jsvar Dictionary=function() { var items={}; // 檢查鍵 this.has=function(key) {  return key in items; } // 添加鍵值對(duì) this.set=function(key, value){  items[key]=value; } // 通過鍵移除元素 this.delete=function(key) {  if(this.has(key)){   delete items[key]   return true  }  return false } // 鍵獲取值 this.get=function(key){  return this.has(key) ? items[key] : undefined; } // 列表返回字典值 this.values=function() {  var values=[];  for(var k in items) {   if(this.has(k)) {    values.push(items[k])   }  }  return values; } // 獲取全部鍵名 this.keys=function() {  return Object.keys(items); } // 獲取私有變量items this.getItems=function() {  return items; }}

          Map數(shù)據(jù)結(jié)構(gòu),它類似于對(duì)象,也是鍵值對(duì)的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對(duì)象)都可以當(dāng)作鍵。

          13.Object.assign(對(duì)象的拷貝)

          Object.assign() 方法用于將所有可枚舉屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象。

          const target={ a: 1, b: 2 };const source={ b: 4, c: 5 };?const returnedTarget=Object.assign(target, source);?console.log(target);// expected output: Object { a: 1, b: 4, c: 5 }?console.log(returnedTarget);// expected output: Object { a: 1, b: 4, c: 5 }?> Object { a: 1, b: 4, c: 5 }> Object { a: 1, b: 4, c: 5 }

          語法

          Object.assign(target, ...sources)

          參數(shù)

          target

          目標(biāo)對(duì)象

          sources

          源對(duì)象

          返回值

          目標(biāo)對(duì)象。

          const obj={ a: 1 };const copy=Object.assign({}, obj);console.log(copy); // { a: 1 }
          • Object.assign()拷貝的是(可枚舉)屬性值
          • Object.assign方法的第一個(gè)參數(shù)是目標(biāo)對(duì)象,后面的參數(shù)都是源對(duì)象
          • 如果目標(biāo)對(duì)象與源對(duì)象有同名屬性,或多個(gè)源對(duì)象有同名屬性,則后面的屬性會(huì)覆蓋前面的屬性
          • 由于undefined和null無法轉(zhuǎn)成對(duì)象,所以如果它們作為參數(shù),就會(huì)報(bào)錯(cuò)
          • 如果undefined和null不在首參數(shù),就不會(huì)報(bào)錯(cuò)
          • 如果源對(duì)象某個(gè)屬性的值是對(duì)象,那么目標(biāo)對(duì)象拷貝得到的是這個(gè)對(duì)象的引用(這個(gè)對(duì)象的任何變化,都會(huì)反映到目標(biāo)對(duì)象上面。)
          Object.assign(undefined) // 報(bào)錯(cuò)Object.assign(null) // 報(bào)錯(cuò)?let obj={a: 1};Object.assign(obj, undefined)===obj // trueObject.assign(obj, null)===obj // true?const obj1={a: {b: 1}};const obj2=Object.assign({}, obj1);?obj1.a.b=2;obj2.a.b // 2?const target={ a: { b: 'c', d: 'e' } }const source={ a: { b: 'hello' } }Object.assign(target, source)// { a: { b: 'hello' } }?const source={  get foo() { return 1 }};const target={};?Object.assign(target, source)// { foo: 1 }

          Object.assign復(fù)制的是屬性值value,如果屬性值是一個(gè)引用類型,那么復(fù)制的其實(shí)是引用地址,就會(huì)存在引用共享的問題(Object.assign(target,source1,...,sourceN)淺拷貝的過程)

          要點(diǎn):

          function ObjectAssign(target, ...sources) { // 對(duì)第一個(gè)參數(shù)的判斷,不能為undefined和null if(target===undefined || target===null) {  throw my TypeError('error'); } // 將第一個(gè)參數(shù)轉(zhuǎn)為對(duì)象(不是對(duì)象轉(zhuǎn)換為對(duì)象) const targetObj=Object(target); // 將源對(duì)象自身的所有可枚舉屬性復(fù)制到目標(biāo)對(duì)象 for(let i=0; i<sources.length; i++){  let source=sources[i];  // 對(duì)于undefined和null在源中不會(huì)報(bào)錯(cuò),會(huì)直接跳過  if(source !==undefined && source !==null) {   // 將源象轉(zhuǎn)換成對(duì)象   // 需要將源自身的可枚舉數(shù)據(jù)進(jìn)行復(fù)制   // Reflect.ownKeys(obj)   const keysArray=Reflect.ownKeys(Object(source));   for (let nextIndex=0; nextIndex < keysArray.length; nextIndex++) {    const nextKey=keysArray[nextIndex];    // 去除不可枚舉屬性    const desc=Object.getOwnPropertyDescriptor(source,nextKey);    if(desc!==undefined&&desc.enumerable){     targetObj[nextKey]=source[nextKey];    }   }  } } return targetObj;}if(typeof Object.myAssign !=='function'){ Object.defineProperty(Object, 'myAssign', {  value: ObjectAssign,  writable: true,  enumerable: false,  configurable: true });}

          淺拷貝 Object.assign 的實(shí)現(xiàn)原理

          拷貝第一層的基本類似值和第一層的引用類型地址:

          let da1={ name: 'da1', age: 1}?let da2={ name: 'da2', study: {  title: 'web' }}?let da3=Object.assign(da1,da2);console.log(da3);// {// name: 'da2',// age: 1,// study: { title: 'web' }// }console.log( da1===da3); // true?da2.name='da22';da2.study.title='web2';console.log(da2);// {// name: 'da22',// study: { title: 'web2' }// }?console.log(da1);// {// age: 1,// name: 'da2',// study: { title: 'web2' }// }

          如果源對(duì)象的屬性值是一個(gè)指向?qū)ο蟮囊茫仓豢截愡@個(gè)引用地址哦!

          let da1={ name: 'da1', age: 1}let da2={ a: Symbol('dadaqianduan'), b: null, c: undefined}let da3=Object.assign(da1, da2);console.log(da3);// {// name: 'da1',// age: 1,// a: Symbol('dadaqianduan'),// b: null,// c: undefined// }console.log(da1===da3); // true
          let map=new Map([iterable])// Map是用來實(shí)現(xiàn)字典的功能-Object鍵值對(duì)

          動(dòng)態(tài)屬性鍵

          // ES5 codevar  key1='one',  obj={    two: 2,    three: 3  };?obj[key1]=1;?// obj.one=1, obj.two=2, obj.three=3?// ES6 codeconst  key1='one',  obj={    [key1]: 1,    two: 2,    three: 3  };?// obj.one=1, obj.two=2, obj.three=3?// ES6 codeconst  i=1,  obj={    ['i' + i]: i  };?console.log(obj.i1); // 1

          補(bǔ)充:前端面試考點(diǎn),HTML和CSS,性能優(yōu)化,原型,作用域,異步,各種手寫代碼,DOM事件和Ajax,HTTP協(xié)議。

          • css(布局,定位,移動(dòng)端響應(yīng)式)
          • es(原型,原型鏈,作用域,閉包,異步,單線程)
          • webapi(DOM BOM,Ajax跨域,事件存儲(chǔ))
          • 開發(fā)環(huán)境(版本管理,調(diào)試抓包,打包構(gòu)建)
          • 運(yùn)行環(huán)境(頁面渲染,性能優(yōu)化,web安全)
          • 網(wǎng)絡(luò)通訊
          1. 布局(盒模型,BFC,float,flex)
          2. 定位,圖文樣式,移動(dòng)端響應(yīng)式(rem,media query,vw/vh),動(dòng)畫、漸變
          3. 變量類型和計(jì)算(值類型和引用類型,類型判斷,邏輯運(yùn)算)
          4. 原型和原型鏈(class,繼承,原型,原型鏈,instanceof)
          5. 作用域和閉包(作用域,自由變量,閉包,this)
          6. 異步(單線程,callback,應(yīng)用場(chǎng)景,Promise,event-loop,async/await,微任務(wù)/宏任務(wù))
          7. 模塊化(ES6 Module)
          8. DOM(樹形結(jié)構(gòu),節(jié)點(diǎn)操作,屬性,樹結(jié)構(gòu)操作,性能)
          9. BOM(navigator,screen,location,history)
          10. 事件(綁定,冒泡,代理)
          11. ajax(XMLHttpRequest,狀態(tài)碼,跨域)
          12. 存儲(chǔ)(cookie,localStorage,sessionStorage)
          13. 開發(fā)環(huán)境(git,調(diào)試,webpack和babel,linux命令)
          14. 運(yùn)行環(huán)境(頁面加載:加載,渲染。性能優(yōu)化:加載資源優(yōu)化,渲染優(yōu)化。安全:xss,CSRF)
          15. HTTP協(xié)議:狀態(tài)碼,Method,Restful API,headers,緩存策略

          14.模板文字

          模板文字是es2015/es6的新功能,與es5及以下版本相比,可以通過新穎的方式使用字符串,先只需要反引號(hào)代替單引號(hào)或雙引號(hào)即可:

          const module_string=`dadaqianduan`

          它們之所以獨(dú)特是因?yàn)樗鼈兲峁┝撕芏嘤靡?hào)構(gòu)建的普通字符串不具備的功能:

          1. 提供了定義多行字符串的語法;
          2. 提供了一種簡(jiǎn)單的方法來插值字符串中的變量和表達(dá)式
          3. 允許您使用模板標(biāo)簽創(chuàng)建DSL(領(lǐng)域特定的語言)

          使用多行字符串

          在es6之前的版本:

          // 要?jiǎng)?chuàng)建跨越兩行的字符串,必須\在行尾使用字符?const dada='dada \  dadaqianduan'  // 呈現(xiàn)效果:在兩行上創(chuàng)建一個(gè)字符串,但是僅在一行上呈現(xiàn)

          要在多行上呈現(xiàn),則需要使用\n在每行的末尾添加

          const string='dada 魔王哪吒\n \  dadaqianduan'

          使用反引號(hào)打開模板文字后,只需要按enter鍵就行:

          const dada=`dadaqianduan 魔王哪吒`

          在這里請(qǐng)記住空間是有意義的:

          const da=`First            Second`

          使用trim()方法,可以消除第一個(gè)字符之前的任何空格

          插補(bǔ):模板文字提供了一種將變量和表達(dá)式插入字符串的簡(jiǎn)便的方法

          const da=`dadaqianduan ${mydada}`?${}里面可以添加任何東西?const da1=`dada ${1+2+3}`const da2=`dada ${dafun() ? 'x' : 'y'}`

          15.什么是解構(gòu)賦值

          let da=['hello', 'world']let [firstName, surName]=dacosole.log(firstName, surName);

          解構(gòu)賦值在于賦值,拷貝出來賦值給變量,而賦值的元素本身不會(huì)發(fā)生改變

          默認(rèn)值

          let [da1, da2]=[];?console.log(da1); // undefinedconsole.log(da2); // undefined

          給變量賦值(默認(rèn)值),防止出現(xiàn)undefined的情況:

          let [da1='da1', da2='da2']=['dadaqianduan]?console.log(da1); // dadaqianduanconsole..log(da2); // da2

          解構(gòu)分配

          ES5中的索引提取這些值:

          var myArray=['a', 'b', 'c'];var  one=myArray[0],  two=myArray[1],  three=myArray[2];?// one='a', two='b', three='c'

          ES6解構(gòu)允許使用更簡(jiǎn)單方法:

          const [one, , three]=myArray;?// one='a', three='c'

          使用rest運(yùn)算符(...)提取剩余元素:

          const [one, ...two]=myArray;?// one='a', two=['b, 'c']
          const myObject={  one:   'a',  two:   'b',  three: 'c'};?// ES6 destructuring exampleconst {one: first, two: second, three: third}=myObject;?// first='a', second='b', third='c'

          可變值交換

          var a=1, b=2;?// ES5 swapvar temp=a;a=b;b=temp;?// a=2, b=1?// ES6 swap back[a, b]=[b, a];?// a=1, b=2?[b, c, d, e, a]=[a, b, c, d, e];

          在ES6中,我們可以為任何參數(shù)分配默認(rèn)值

          function dada(param={}) {

          函數(shù)返回多個(gè)值(函數(shù)只能返回一個(gè)值,但可以是一個(gè)復(fù)雜的對(duì)象或多維數(shù)組)

          function f() {  return [1, 2, 3];}?const [a, b, c]=f();?// a=1, b=2, c=3

          ES6 JavaScript深度解構(gòu)

          默認(rèn)情況下,找不到的屬性為undefined

          var {da}={bar: 'dada'}console.log(da)// undefined

          如果訪問不存在的父級(jí)的深層嵌套屬性,則將獲得異常。

          var {da:{bar}}={dada: 'dadaqianduan'}// Exception
          var key='dadaqianduan'var { [key]: foo }={ dadaqianduan: 'bar' }console.log(foo)// 'bar'
          var {da=3}={ da: 2 }console.log(da)// 2var {da=3}={ da: undefined }console.log(da)// 3var {da=3}={ bar: 2 }console.log(da)// 3?var [a]=[]console.log(a)//  undefinedvar [b=10]=[undefined]console.log(b)//  10var [c=10]=[]console.log(c)//  10?function da () {  return {    x: 1,    y: 2  }}var {x, y}=da()console.log(x)// 1console.log(y)// 2

          16.異步操作

          Callback

          Promise

          function loadScript(src) { return new Promise((resolve, reject)=> {  let script=document.createElement('script')  script.src=src  script.onload=()=> resolve(src)  script.onerror=(err)=> reject(err)  document.head.append(script) })}
          function loadScript(src) { let script=document.createElement('script'); script.src=src; document.head.append(script)}
          var promise=new Promise(function(resolve, reject){ resolve('傳遞給then的值')})promise.then(function(value){ console.log(value)},function(error){ console.error(error)})

          Promise對(duì)象是用于表示一個(gè)異步操作的最終完成(或失敗),以及其結(jié)果值。

          示例:

          const promise=new Promise((resolve, reject)=> { setTimeout(()=> {  resolve('da'); }, 200);});?promise.then((value)=> { console.log(value);});?console.log(promise);

          語法:

          new Promise(function (resolve,reject){...});

          描述:Promise對(duì)象是一個(gè)代理對(duì)象,被代理的值在Promise對(duì)象創(chuàng)建時(shí)可能是未知的,它允許你為異步操作的成功和失敗分別綁定相應(yīng)的處理方法,這讓異步方法可以像同步方法那樣返回值,但并不是立即返回最終執(zhí)行結(jié)果,而是一個(gè)能代表未來出現(xiàn)的結(jié)果的promise對(duì)象。

          一個(gè)Promise有以下幾種狀態(tài):

          1. pending,初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
          2. fulfilled,意味著操作成功完成。
          3. rejected,意味著操作失敗。

          pending狀態(tài)的Promise對(duì)象可能會(huì)變?yōu)閒ulfilled狀態(tài)并傳遞一個(gè)值給相應(yīng)的狀態(tài)處理方法。

          Promise.prototype.then和Promise.prototype.catch方法返回promise對(duì)象,所以它們可以被鏈?zhǔn)秸{(diào)用。

          方法:

          Promise.all(iterable)

          1. 返回一個(gè)新的promise對(duì)象
          2. 在iterable參數(shù)對(duì)象里所有的promise對(duì)象都成功時(shí),才會(huì)觸發(fā)成功
          3. 當(dāng)任何一個(gè)iterable里面的promise對(duì)象失敗,才會(huì)觸發(fā)promise對(duì)象的失敗
          4. 成功狀態(tài)會(huì)把一個(gè)包含iterable里所有promise返回值的數(shù)組作為成功回調(diào)的返回值,順序和iterable的順序一樣
          5. 如果這個(gè)新的promise對(duì)象觸發(fā)了失敗,會(huì)把iterable里的第一個(gè)觸發(fā)失敗的promise對(duì)象的錯(cuò)誤信息作為它的失敗信息
          6. 場(chǎng)景,多用于處理多個(gè)promise對(duì)象的狀態(tài)集合

          Promise.any(iterable)

          1. 接收一個(gè)Promise對(duì)象的集合,當(dāng)其中的一個(gè)promise成功,就返回那個(gè)成功的promise的值

          Promise.reject(reason)

          1. 返回一個(gè)狀態(tài)為失敗的Promise對(duì)象,然后將失敗信息傳遞給對(duì)應(yīng)的處理方法

          Promise.resolve(value)

          1. 返回一個(gè)狀態(tài)由給定value決定的Promise對(duì)象

          Promise原型

          屬性:Promise.prototype.constructor返回被創(chuàng)建的實(shí)例函數(shù),默認(rèn)為Promise函數(shù)。

          方法:

          • Promise.prototype.catch(onRejected)
          • Promise.prototype.then(onFulfilled,onRejected)
          • Promise.prototype.finally(onFinally)
          function myAsyncFunction(url) { return new Promise((resolve, reject)=> {  const xhr=new XMLHttpRequest();  xhr.open('GET',url);  xhr.onload=()=> resolve(xhr.responseText);  xhr.onerror=()=> reject(xhr.statusText);  xhr.send(); });}

          17.ES6代理

          1. 默認(rèn)情況下,代理不執(zhí)行任何操作

          示例:

          var target={}var handler={}var proxy=new Proxy(target, handler)proxy.a='b'console.log(target.a)// 'b'console.log(proxy.c===undefined)// true

          為了更好地了解代理的有用性,讓我們做一些小練習(xí)。

          示例:

          想象一下,您已經(jīng)17歲了,即將滿18歲。并且您希望您的程序在打開時(shí)自動(dòng)向您祝賀。為此,您可以使用代理。

          var person={  name: "dada",  age: 17};?person=new Proxy(person, {  set(target, property, value) {    if (value===18) {      console.log("Congratulations! You are of legal age");      Reflect.set(target, property, value);      return true;    }  }});?person.age=18;?if (value < 13 && value > 99) {  throw new Error('The age should be between 13 and 99')} else {  Reflect.set(target, property, value)}

          語法:

          let p=new Proxy(target, handler)
          1. target 用Proxy包裝的目標(biāo)對(duì)象
          2. handler 一個(gè)對(duì)象,其屬性是當(dāng)執(zhí)行一個(gè)操作時(shí)定義代理的行為的函數(shù)

          如果不想再調(diào)用key的時(shí)候,返回undefined:

          console.log(o.dada || '')

          使用Proxy

          let o={ name:'dada', age: 1}?let handler={ get(obj, key) {  return Reflect.has(obj, key)?obj[key]:'' }}?let p=new Proxy(o, handler)?console.log(p.from)

          希望從服務(wù)器獲取的數(shù)據(jù)只讀,不允許修改:

          for (let [key] of Object.entries(response.data)) {  Object.defineProperty(response.data, key, {  writable: false })}

          使用Proxy:

          let data=new Proxy(response.data, { set(obj, key, value) {   return false }})

          檢驗(yàn)邏輯代碼:

          // Validator.js?export default(obj, key, vlaue)=> { if(Reflect.has(key) && value > 20) {  obj[key]=value }}?import Validator from './Validator'?let data=new Proxy(response.data, { set: Validator})

          使用Proxy,對(duì)讀寫進(jìn)行監(jiān)控:

          let validator={ set(target, key, value) {  if(key==='age') {   if(typeof value !=='number' || Number.isNaN(value)) {    throw new TypeError('Age must be a number')   }   if(value<=0){    throw new TypeError('Age must be a positive number')   }  }  return true }}?const person={ age: 12 }const proxy=new Proxy(person,validator)proxy.age='dada' // TypeError numberproxy.age=NaNproxy.age=0 // positive numberproxy.age=3

          示例:每個(gè)對(duì)象都有一個(gè)自己的id

          class Component { constructor() {  this.proxy=new Proxy({   id: Math.random().toString(36).slice(-8)  }) } get id() {  return this.proxy.id }}

          18.Generator

          function * dada() { for(let i=0; i<2; i++ {  yield console.log(i); }}?const da=dada()da.next()da.next()

          Generator函數(shù)與普通函數(shù)的區(qū)別在于定義的時(shí)候有一個(gè)*,執(zhí)行下面函數(shù):

          function* dada() {console.log('dadaqianduan');}dada(); // 沒有執(zhí)行函數(shù) 如需要輸出,改為:var da=dada();da.next();

          要生成一個(gè)自增的id:

          var count_id=0;function dada_id() {count_id ++;return count_id;}

          方法

          Generator.prototype.next()返回一個(gè)由 yield表達(dá)式生成的值。?Generator.prototype.return()返回給定的值并結(jié)束生成器。?Generator.prototype.throw()向生成器拋出一個(gè)錯(cuò)誤。

          書寫風(fēng)格:

          function *da() {}?function* da(){}

          方法

          Generator對(duì)象方法:next,return,throw

          通過Next方法來獲取每一次遍歷的結(jié)果,這個(gè)方法返回一個(gè)對(duì)象,這個(gè)對(duì)象包含兩個(gè)value和done。

          value:當(dāng)前程序的運(yùn)行結(jié)果 done:遍歷是否結(jié)束

          next是可以接收參數(shù)的,這個(gè)參數(shù)可以讓你在generator外部給內(nèi)部傳遞數(shù)據(jù),這個(gè)參數(shù)就是作為yield的返回值。

          return()方法可以讓generator遍歷終止

          function * da() { yield 1 yield 2 yield 3}var d=da()console.log(d.next()) // {value:1,done:false}console.log(d.return()) // {value:undefined,done:true}console.log(d.next()) // {value:undefined,done:true}

          return可以傳入?yún)?shù),作為返回的value的值

          function * da() { yield 1 yield 2 yield 3}var d=da()console.log(d.nex()) // {value:1,done:false}console.log(d.return(100)) // {value:100,done:true}console.log(d.next()) // {value:undefined,done:true}

          throw()方法在generator外部控制內(nèi)部執(zhí)行的“終斷”

          generator函數(shù)聲明:

          function* genFunc(){...}const genObj=genFunc();

          generator表達(dá)式:

          const genFunc=function* () {...}const genObj=genFunc();

          對(duì)象中定義:

          const obj={ * generatorMethod(){  ... }}const genObj=obj.generatorMethod();

          類定義(類聲明或類表達(dá)式):

          class MyClass{ * generatorMethod(){  ... }}const myInst=new MyClass();const genObj=myInst.generatorMethod();

          最簡(jiǎn)單的iterator遍歷規(guī)范:

          authors[Symbol.iterator]=function(){ // this return {  next(){   return{    done:false,    value:1   }  } }}

          19.module

          在es6前,js文件之間的導(dǎo)入,導(dǎo)出是借助require.js,sea.js,如現(xiàn)在使用import,export,來實(shí)現(xiàn)原生javascript的導(dǎo)入,導(dǎo)出。

          export:

          導(dǎo)出變量或者常量?export const da='dadaqianduan'export let da1='da1'export var da2='da1'?const name='dada'let name1='dada1'export { name, name1}?導(dǎo)出函數(shù)export function da(value){ console.log(value)}?const da=(value)=> { console.log(value);}?export { da}?導(dǎo)出Objectexport({ name: 'da1', message: 'dadaqianduan'})?let da={ name: 'name1'}export { da}?導(dǎo)出Classclass Da { constructor(){  this.id=1 }}export { Da}?export class Da { constructor() {  this.id=1 }}?修改導(dǎo)出名稱const name='da1'export { name as cname}export default name

          import

          // 直接導(dǎo)入const name='dada'let name1='dada1'var name2='dada2'export { name as cname}export default name2?import name2, {name1, name} from A
          export const sqrt=Math.sqrt;export function square(x) { return x * x;}export function dada(x,y) { return sqrt(square(x) + square(y));}?import {square,da} from 'da';console.log(square(11)); // 121console.log();
          export default function() {...}import myFunc from 'myFunc';export default class{...}import MyClass from 'MyClass';const inst=new MyClass();

          20.import, export

          require--lib.js--function add(x,y){ return x + y}module.exports={ add: add,};?--main.js--var add=require('lib').addd;console.log(add(1,2));
          import--lib.js--export function add(x,y) { return x + y}--main.js--import {add} from 'lib';console.log(add(1,2));
          --lib.js--export const sqrt=Math.sqrt;export function square(x) { return x * x;}export function da(x,y) { return sqrt(square(x)+square(y));}--main.js--import {square, da} from 'lib'??--myFunc.js--export default function() {...};--main.js--import myFunc from 'myFunc';myFunc();

          21.Array.prototype.includes,Promise

          該方法判斷一個(gè)數(shù)組是否包含一個(gè)指定的值,返回布爾值

          let da1=[1,2,3];console.log(da1.includes(2));
          arr.find(function(item){return item===1;})?arr.filter(function(item){return item===2;})?Math.pow(2,3)->2**3
          async function firstAsync(){ let promise=new Promise ((resolve,reject)=> {  setTimeout(function(){   resolve('dadaqianduan')  },1000) }) console.log(await promise) console.log(await Promise.resolve(1)) console.log(2) return Promise.resolve(3)}firstAsync().then(val=> { console.log(val)})

          await后面是Promise對(duì)象

          Object.values()返回一個(gè)數(shù)組,其元素是在對(duì)象上找到的可枚舉屬性值。

          let da={ 'da': 1, 'da2': 2}console.log(Object.value(da)) // [1,2]?Object.values是在對(duì)象上找到可枚舉的屬性的值,所以只要這個(gè)對(duì)象是可枚舉的就可以

          Object.entries()方法返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)數(shù)組

          22.JS異步進(jìn)階

          題目一:

          Promise.resolve().then(()=>{ console.log(1)}).catch(()=>{ console.log(2)}).then(()=>{ console.log(3)})

          題目二:

          Promise.resolve().then(()=>{ console.log(1) throw new Error('da')}).catch(()=>{ console.log(2)}).then(()=>{ console.log(3)})

          題目三:

          Promise.resolve().then(()=>{ console.log(1) throw new Error('da')}).catch(()=>{ console.log(2)}).catch(()=>{ console.log(3)})

          題目四:

          async function fn() { return 1}(async function() { const a=fn() // ?? const b=await fn() // ??})()

          題目五:

          console.log(100)setTimeout( ()=> { console.log(200)})Promise.resolve().then( ()=> { console.log(300)})console.log(400)

          題目六:

          async function async1() { console.log('async1 start') await async2() console.log('async1 end')}?async function async2 () { console.log('async2')}?console.log('script start')?setTimeout(function(){ console.log('setTimeout')},0)?async1()?new Promise(function (resolve){ console.log('promise1') resolve()}).then(function(){ console.log('promise2')})?console.log('script end')

          加載圖片:

          // 加載function  loadImg(src) { const p=new Promise(  (resolve,reject)=> {   const img=document.createElement('img')   img.onload=()=>{    resolve(img)   }   img.onerror=()=>{    const err=new Error('圖片加載失敗')    reject(err)   }   img.src=src  } ) return p}const url='https'const p=loadImg(url)?p.then(img=>{ console.log(img.width) return img}).then(img=>{ console.log(img.height)}).catch(ex=> { console.error(ex)})
          async function async1() { console.log('async1 start') // 2 await async2() // undefined console.log('async1 end') // 5}async function async2() { console.log('async2') // 3}console.log('script start') // 1async1()console.log('script end') // 4

          for...of常用于異步的遍歷

          function add(num) { return new Promise(resolve=> {  setTimeout(()=>{   resolve(num*num)  },1000) })}const nums=[1,2,3]nums.forEach(async(i)=>{ const res=await add(i)})

          23.宏任務(wù)和微任務(wù)

          宏任務(wù):setTimeout,setInterval,ajax等 微任務(wù):Promise async/await

          微任務(wù)執(zhí)行時(shí)比宏任務(wù)要早:

          宏任務(wù):DOM渲染后觸發(fā),如setTimeout

          微任務(wù):DOM渲染前觸發(fā),如Promise

          24.For await of 異步操作集合

          function da(time) { return new Promise(function(resolve,reject){  setTimeout(function(){   resolve(time)  },time) })}async function test() { let arr=[da(2000),da(1000),da(3000)] for await (let item of arr) {  console.log(Date.now(), item) }}
          const input={ a: 1, b: 2}const output={ ...input, c: 3}console.log(output)?const input={ a: 1, b: 2, c: 3}let {a, ...rest }=input

          25.Array.prototype.flat()

          該方法會(huì)按照一個(gè)可指定的深度遞歸遍歷數(shù)組,并將所有元素與遍歷到的子數(shù)組中的元素合為一個(gè)新數(shù)組。

          Array.prototype.flat()建議將數(shù)組遞歸展平至指定范圍depth并返回新數(shù)組。

          depth(指定要提取嵌套數(shù)組的結(jié)構(gòu)深度)

          語法:Array.prototype.flat(depth)

          depth —默認(rèn)值1,Infinity用于展平所有嵌套數(shù)組。

          const numbers=[1, 2, [3, 4, [5, 6]]];?// Considers default depth of 1numbers.flat(); ?> [1, 2, 3, 4, [5, 6]]?// With depth of 2numbers.flat(2); ?> [1, 2, 3, 4, 5, 6]?// Executes two flat operationsnumbers.flat().flat(); ?> [1, 2, 3, 4, 5, 6]?// Flattens recursively until the array contains no nested arrays?numbers.flat(Infinity)> [1, 2, 3, 4, 5, 6]

          語法:Array.prototype.flatMap(callback)

          callback:function產(chǎn)生新Array的元素。

          const numbers=[1, 2, 3];?numbers.map(x=> [x * 2]);> [[2], [4], [6]]?numbers.flatMap(x=> [x * 2]);> [2, 4, 6]

          Object.fromEntries

          Object.fromEntries執(zhí)行與的相反操作Object.entries。它將一組鍵值對(duì)轉(zhuǎn)換為一個(gè)對(duì)象。

          const records=[['name','da'], ['age', 32]];?const obj=Object.fromEntries(records);?> { name: 'da', age: 32}?Object.entries(obj);?> [['name','Mathew'], ['age', 32]];

          Symbol.prototype.description

          只讀屬性,返回Symbol對(duì)象的可選描述:

          Symbol('desc').toString();> "Symbol(desc)"?Symbol('desc').description;  > "desc"?Symbol('').description;      > ""?Symbol().description;> undefined

          點(diǎn)關(guān)注,不迷路

          好了各位,以上就是這篇文章的全部?jī)?nèi)容,能看到這里的人都是人才。我后面會(huì)不斷更新技術(shù)相關(guān)的文章,如果覺得文章對(duì)你有用,歡迎給個(gè)“贊”,也歡迎分享,感謝大家 !!


          喜歡本文的朋友,歡迎關(guān)注公眾號(hào) 程序員小灰,收看更多精彩內(nèi)容

          日,在世紀(jì)之初風(fēng)靡全球、一時(shí)風(fēng)頭無兩的女星“小甜甜”布蘭妮·斯皮爾斯再次出現(xiàn)在公眾視野。但這一次,不是因?yàn)樗屓f人空巷的全球巡演,不是因?yàn)樗裏徜N3000萬張的個(gè)人專輯,而是因?yàn)樗?qǐng)求法院解除父親杰米·斯皮爾斯對(duì)自己的永久監(jiān)護(hù)權(quán)。

          “小甜甜”布蘭妮·斯皮爾斯。

          在6月23日的庭審中,布蘭妮通過語音連線,向法官講述了過去13年中,自己遭受父親及其工作團(tuán)隊(duì)的虐待,具體內(nèi)容包括被迫長(zhǎng)時(shí)間、高強(qiáng)度工作,接受非自愿并有較大副作用的精神治療,安置節(jié)育環(huán)以致她無法再次懷孕。此外,處于監(jiān)護(hù)狀態(tài)下的她,完全喪失了對(duì)自己財(cái)產(chǎn)的使用權(quán)利,連簡(jiǎn)單的度假、美甲、按摩等需求都無法得到父親批準(zhǔn),“在加州,只有被關(guān)起來的性工作者才這么慘——信用卡、現(xiàn)金、手機(jī)、護(hù)照全部被收走。”

          在通話的最后,布蘭妮說道:“我希望我能一直和你聊下去。我生怕一掛掉電話,就會(huì)立刻回到被人全盤否定的狀態(tài)。我覺得自己被排擠、被欺負(fù)、被冷落……我受夠了這種孤獨(dú)的狀態(tài)。我值得擁有和任何人一樣的權(quán)利,我也想有孩子、有家庭、有自己的人生……”

          2008年,布蘭妮因使用精神類藥物而被強(qiáng)制送往精神醫(yī)療中心治療。之后,她的父親杰米向法院申請(qǐng),成為了布蘭妮的“永久監(jiān)護(hù)人(Conservator)”。根據(jù)美國(guó)法律,“監(jiān)護(hù)(Conservatorship)”是一種將個(gè)體的個(gè)人、經(jīng)濟(jì)和法律決策權(quán)轉(zhuǎn)讓給他人的機(jī)制。對(duì)于因年老癡呆、身體或精神缺陷而無法自主作出決定的成年人,法院可以任命一位法定監(jiān)護(hù)人,負(fù)責(zé)監(jiān)督他的日常活動(dòng),如醫(yī)療保健或生活安排,并對(duì)它的財(cái)務(wù)事務(wù)進(jìn)行托管。

          此后13年,布蘭妮便一直處于被父親“監(jiān)護(hù)”的狀態(tài)之下。盡管在此期間,她發(fā)行了4張專輯,其中2張成為白金唱片(在美國(guó),專輯銷量超過100萬張即可被認(rèn)證為“白金唱片”),并在2018年進(jìn)行了全球巡回演出,但法院仍判定,“她的精神狀態(tài)無法自主作出明智的決定”。

          在社交網(wǎng)絡(luò)上,由布蘭妮粉絲發(fā)起的“釋放布蘭妮(Free Britney)”運(yùn)動(dòng)已進(jìn)行多年。自2020年起,布蘭妮本人也試圖通過法律途徑擺脫被父親監(jiān)護(hù)的狀態(tài)。

          而這一次,布蘭妮本人通過連線出庭,聲淚俱下地講述了自己這13年的遭遇。這不僅牽動(dòng)了成千上萬關(guān)注她的粉絲的心,也讓更多粉絲之外的法律研究者、婦女權(quán)利保護(hù)者乃至美國(guó)國(guó)會(huì)議員開始反思“監(jiān)護(hù)人”制度的正當(dāng)性。

          同時(shí),布蘭妮的遭遇也引發(fā)了不少好萊塢工作者的共情。在他們看來,是娛樂圈長(zhǎng)期盛行的厭女風(fēng)氣,加之狗仔娛記和大眾審視的步步緊逼,共同造成了布蘭妮的悲劇。

          為什么身為成年人、具有基本工作能力的布蘭妮還要被“監(jiān)護(hù)”?是什么讓她被判定為“精神失常”,被媒體和輿論塑造為一個(gè)“瘋女人”?誰該為布蘭妮的悲劇負(fù)責(zé)?本文系統(tǒng)梳理了國(guó)內(nèi)外多家媒體以及相關(guān)領(lǐng)域?qū)<业母鞣接^點(diǎn)。

          撰文 | 肖舒妍

          備受爭(zhēng)議的監(jiān)護(hù)權(quán):是保護(hù)還是虐待?

          “如果我能工作、賺錢、還能付錢給別人,我就不應(yīng)該被監(jiān)護(hù),”在6月23日的庭審中,布蘭妮提出了這一觀點(diǎn),“這毫無意義。法律需要改變。”

          最初,布蘭妮是如何“陷入”監(jiān)護(hù)之中的?

          2007年,布蘭妮的狀態(tài)一度滑入低谷。飽受輿論壓力的她先是在一家理發(fā)店公開剃光了自己的頭發(fā),對(duì)著鏡頭大喊“我受夠了別人不停碰我!”緊接著又陷入了和前夫凱文·費(fèi)德勒爭(zhēng)奪兩個(gè)孩子撫養(yǎng)權(quán)的官司。她失去了孩子的獨(dú)立撫養(yǎng)權(quán),僅獲得探視權(quán)。在一次探望孩子卻被前夫拒之門外之后,面對(duì)狗仔的長(zhǎng)槍短炮和不斷追問,布蘭妮情緒崩潰,舉起一把雨傘企圖趕走狗仔,卻被狗仔拍下了她失控的狀態(tài),放上雜志封面。一時(shí)間,布蘭妮“陷入瘋癲”、成為“瘋女人”的傳聞便不絕于耳。

          2007年2月,布蘭妮情緒失控之后,在理發(fā)店公開剃光了自己的頭發(fā)。

          次年一月的一個(gè)晚上,費(fèi)德勒來到布蘭妮家中,準(zhǔn)備接走兩個(gè)孩子,卻發(fā)現(xiàn)布蘭妮把自己和兒子鎖在衛(wèi)生間,不肯開門。警方趕到后,發(fā)現(xiàn)布蘭妮“受到精神類藥物影響”,于是將其強(qiáng)制送往精神醫(yī)療中心治療。在她住院期間,杰米·斯皮爾斯向法院提交申請(qǐng),獲得了女兒的“臨時(shí)監(jiān)護(hù)權(quán)”。在2008年10月,“臨時(shí)監(jiān)護(hù)權(quán)”又轉(zhuǎn)為“永久監(jiān)護(hù)權(quán)”。

          盡管此后布蘭妮的精神狀態(tài)有所好轉(zhuǎn),監(jiān)護(hù)托管卻并未因此終止。熟悉此案的律師Vivian Thoreen在《紐約時(shí)報(bào)》拍攝的紀(jì)錄片《陷害布蘭妮》(Framing Britney Spears)中提到,如果被監(jiān)護(hù)人想要終止監(jiān)護(hù),需要向法院提交一份請(qǐng)?jiān)笗⑻峁┳约阂巡恍枰O(jiān)護(hù)(或監(jiān)護(hù)已經(jīng)失效)的證據(jù),但在她所參與的所有案件中,沒有一個(gè)被監(jiān)護(hù)人曾成功終止監(jiān)護(hù),“一旦進(jìn)入監(jiān)護(hù)系統(tǒng),就很難再有可能脫身”。

          “如果個(gè)人想要終止監(jiān)護(hù),責(zé)任應(yīng)該由反對(duì)終止的一方承擔(dān)(以證明監(jiān)護(hù)有必要繼續(xù)),但實(shí)際上,通常情況下,往往是個(gè)人必須證明他們不再需要被監(jiān)護(hù)。”卡多佐法學(xué)院(Cardozo School of law)的臨床法學(xué)教授、監(jiān)護(hù)法專家萊斯利·薩爾茲曼(Leslie Salzman)這樣表示。

          “就像‘第22條軍規(guī)’的規(guī)定,瘋子可以免于飛行任務(wù),但必須由飛行員本人提出申請(qǐng),而本人一旦提出申請(qǐng),便可證明他并不是瘋子。想從監(jiān)護(hù)中脫身,布蘭妮必須要證明自己有管理生活以及財(cái)產(chǎn)的能力,但她正處于監(jiān)護(hù)中,也就意味著她沒有上述能力。” 支持布蘭妮的導(dǎo)演史塔克向《綜藝》雜志說道。

          一個(gè)精神正常的人該如何證明自己精神正常呢?如果布蘭妮在監(jiān)護(hù)狀態(tài)下身心健康,則可以解釋為“監(jiān)護(hù)制度行之有效”;如果布蘭妮對(duì)監(jiān)護(hù)狀態(tài)掙扎抵抗,又可以說明“她無法做出理智判斷,必須接受監(jiān)護(hù)”;在監(jiān)護(hù)狀態(tài)下,布蘭妮犯的任何一個(gè)小錯(cuò)誤,都能夠作為她不宜解除監(jiān)護(hù)的證據(jù)。但即使一個(gè)身心健全的普通人,也并不總能做出符合個(gè)人最大利益的明智決定。

          “如果一個(gè)普通人決定,‘我要休假,喝幾杯酒,吃一大堆甜甜圈,然后小睡一會(huì)兒,’這是很正常的事情,屬于個(gè)人基本權(quán)利,”記者Sara Luterman舉例論證,“但如果你有精神疾病或生理缺陷,這就會(huì)被視為你‘沒有能力負(fù)責(zé)任地管理自己的生活’的進(jìn)一步證據(jù)。”

          在2020年的庭審中,法官在判決時(shí)提出,布蘭妮的情況很特殊,她是一名“具有高能力的受監(jiān)護(hù)人”。許多人對(duì)此感到疑惑,這顯然是個(gè)自相矛盾的詞。

          另一方面,布蘭妮的監(jiān)護(hù)人人選也飽受質(zhì)疑。杰米·斯皮爾斯雖然是布蘭妮的父親,卻在2008年之前長(zhǎng)期缺席她的人生,與妻子離婚多年,甚至找不到一張他和女兒合影。此外,他曾因酗酒被送進(jìn)強(qiáng)制康復(fù)機(jī)構(gòu),多次創(chuàng)業(yè)失敗最終申請(qǐng)破產(chǎn)。在紀(jì)錄片《陷害布蘭妮》中,布蘭妮曾經(jīng)的唱片營(yíng)銷總監(jiān)回憶:“我不能判斷杰米是個(gè)怎樣的人,我只見過他一面。他對(duì)我說過唯一的一句話就是:‘我女兒會(huì)變得很富有,她會(huì)給我買艘大船。’”

          布蘭妮的父親杰米·斯皮爾斯。(圖源:紀(jì)錄片《陷害布蘭妮》)

          無論是布蘭妮的母親林恩(Lynn),還是自她5歲起就認(rèn)識(shí)她、之后一直陪伴她參加活動(dòng)、幫助她打理事業(yè)的長(zhǎng)期助理Felicia Culotta,都比杰米更加了解、關(guān)心布蘭妮的生活狀態(tài)。在進(jìn)入監(jiān)護(hù)狀態(tài)之后,布蘭妮曾多次向法院提出更換監(jiān)護(hù)人,在2020年更表示,如果杰米繼續(xù)掌控她的事業(yè),她將拒絕演出。

          “雖然受監(jiān)護(hù)人未必能夠選擇自己的監(jiān)護(hù)人,但他們當(dāng)然可以推薦并說明希望誰成為監(jiān)護(hù)人。法院應(yīng)該對(duì)這一請(qǐng)求給予充分考慮。考慮到監(jiān)護(hù)人要托管受監(jiān)護(hù)人的個(gè)人事務(wù),他要能夠被對(duì)方所接受。”薩爾茲曼這樣認(rèn)為。而在布蘭妮的案例中,她的需求顯然沒有被尊重。

          布蘭妮的支持者要求法院終止她的“被監(jiān)護(hù)”狀態(tài)。

          加之布蘭妮作為全球巨星所擁有的高額財(cái)產(chǎn),進(jìn)一步提高了監(jiān)護(hù)權(quán)的復(fù)雜程度。布蘭妮的公開資產(chǎn)為6000萬美元,本身極為可觀,同時(shí)有媒體披露,布蘭妮的實(shí)際財(cái)產(chǎn)超過6億美元,但大部分均被杰米轉(zhuǎn)移。此外,布蘭妮還要提供自己全部收入的1.5%作為“監(jiān)護(hù)傭金”。很難判斷對(duì)杰米而言,女兒是需要照顧的受監(jiān)護(hù)人,還是日進(jìn)斗金的搖錢樹。

          布蘭妮在法庭陳述中提到,自己被要求每周工作7天,每天工作10小時(shí),2018年的巡回演出和在拉斯維加斯長(zhǎng)達(dá)4年的駐唱都是被迫簽約,即便在發(fā)燒40度時(shí)她也要上臺(tái)演出。根據(jù)《紐約時(shí)報(bào)》的調(diào)查,布蘭妮在拉斯維加斯的演出總票房高達(dá)1.37億美元,但屬于布蘭妮的分成只有每周2000美元。

          而在2020年接受法院?jiǎn)栐儠r(shí),杰米團(tuán)隊(duì)的律師更提出,他們將采用一種“全新的混合型商業(yè)模式”,幫助布蘭妮完成事業(yè)開拓和財(cái)富增長(zhǎng)。有團(tuán)隊(duì)表示,監(jiān)護(hù)團(tuán)隊(duì)成立時(shí),布蘭妮的資產(chǎn)只有幾百萬美元,而現(xiàn)在已超過6000萬美元,這證明了團(tuán)隊(duì)的監(jiān)護(hù)工作行之有效。但他們似乎沒有意識(shí)到,托管成立的初衷,是為了幫助布蘭妮恢復(fù)身心健康,而不是讓她成為賺錢機(jī)器。

          在布蘭妮與杰米對(duì)簿公堂時(shí),她不僅要支付自己的治療費(fèi)用、自己的法務(wù)費(fèi)用,還要支付杰米團(tuán)隊(duì)的工資,杰米方的法務(wù)費(fèi)用。

          杰奎琳·布徹(Jacqueline Butcher)曾是斯皮爾斯家族的好友,并在2008年提供證詞幫助杰米獲得監(jiān)護(hù)權(quán)。而現(xiàn)在,她對(duì)自己過去的行為表示遺憾,“當(dāng)時(shí)我以為自己是在幫忙,但實(shí)際上我?guī)鸵粋€(gè)腐敗的家族掌控了一切。”

          “盡管布蘭妮的情況看起來非常極端并令人不安,” Erica Schwiegershausen在《紐約》雜志中寫道,“她講述的大部分內(nèi)容——例如未經(jīng)同意接受藥物治療、接受非自愿精神病評(píng)估和被強(qiáng)制送進(jìn)精神病院——對(duì)任何有精神疾病經(jīng)歷的人來說都很熟悉。”據(jù)估計(jì),在美國(guó)有數(shù)以百萬計(jì)的智力缺陷或社交能力缺陷者被剝奪了法律行為能力,處于某種形式的監(jiān)護(hù)之下。這導(dǎo)致了一系列虐待行為,包括強(qiáng)制醫(yī)療、強(qiáng)制避孕、強(qiáng)制終止妊娠、非自愿監(jiān)禁、強(qiáng)制生活安排和行動(dòng)自由受限。

          紀(jì)錄片《陷害布蘭妮》劇照。

          父權(quán)控制與女性反抗:一場(chǎng)漫長(zhǎng)的斗爭(zhēng)

          在布蘭妮所遭受的一系列限制和虐待中,“強(qiáng)制避孕”一點(diǎn)尤其令人不解,無論從任何角度都無法看作是對(duì)于布蘭妮的保護(hù)。在法庭陳述中,布蘭妮說道:“我希望能夠結(jié)婚生子,但這卻被監(jiān)護(hù)團(tuán)隊(duì)所禁止。我的體內(nèi)放有宮內(nèi)節(jié)育器,因此無法懷孕。我想取出宮內(nèi)節(jié)育器,再生一個(gè)孩子,可是這個(gè)所謂的團(tuán)隊(duì)不讓我接近醫(yī)生。”

          限制布蘭妮生育這一行為,幾乎遭到了美國(guó)各黨派人士的譴責(zé),包括美國(guó)計(jì)劃生育行動(dòng)基金董事會(huì)主席亞歷克西斯·麥吉爾·約翰遜(Alexis McGill Johnson),保守派共和黨參議員特德克魯茲(Ted Cruz)和眾議員南希·梅斯(Nancy Mace)、卡羅琳·馬洛尼(Carolyn B.Maloney)。

          “你可以打著保護(hù)的幌子強(qiáng)迫一個(gè)女人絕育,這太瘋狂了。如果這能發(fā)生在布蘭妮·斯皮爾斯身上,全國(guó)還會(huì)有多少其他女人在默默受苦?” 眾議員南希·梅斯在推特上寫道。

          “從一開始,男人就控制著女人的身體、思想和抱負(fù),”另一位眾議員卡羅琳·馬洛尼則向《華盛頓郵報(bào)》表示,“斯皮爾斯女士和世界各地的任何女性一樣,理應(yīng)對(duì)自己的身體、權(quán)益和財(cái)產(chǎn)享有完全的自主權(quán)。”

          這一控制在布蘭妮身上顯得尤其讓人心碎。在出道伊始,布蘭妮的形象便與獨(dú)立、自主、強(qiáng)大聯(lián)系在一起。在早年的采訪視頻中,她笑著告訴記者:“我知道自己所有的合同,我知道自己所有要做的工作,我才不是那種只聽經(jīng)紀(jì)人話的女孩。”

          曾與布蘭妮合作過的伴舞也多次提到:“她絕不是牽線木偶。她是老大(boss)。”對(duì)于演出造型、舞蹈動(dòng)作、演唱歌曲,布蘭妮都會(huì)堅(jiān)定而自信地提出自己的看法。

          在粉絲看來,布蘭妮展示出的“女人的我行我素,不必再取悅他人”,正是她最大的魅力所在。“布蘭妮的形象不再只是父權(quán)制下少女形象的意義典范,而是父權(quán)控制與女性抵抗,甚至資本主義與個(gè)體之間的符號(hào)沖突(semiotic struggle)的表現(xiàn)。”公眾號(hào)“看理想”在一篇文章中如此概括布蘭妮的文化意義。

          但是當(dāng)時(shí)的美國(guó)大眾,一方面狂熱地喜愛這樣一個(gè)“既性感又純潔、既乖巧又獨(dú)立”的熒幕形象,一方面卻又沒做好準(zhǔn)備接受這樣的女性走出熒幕、走進(jìn)生活。“在‘圣經(jīng)地帶’出生、長(zhǎng)大的布蘭妮,身上集中了美國(guó)人的兩種期望:既要穿著性感,給人曖昧的想象,又要保持純潔的處女身;既要盡可能地觸探性的界限,又絕不能越雷池半步。兩種期望顯然是矛盾的,這讓她受到了嚴(yán)格的形象、行為、道德審查。” 作者李孟蘇在“三聯(lián)生活周刊”公眾號(hào)文章中寫道。

          紀(jì)錄片《陷害布蘭妮》截圖。

          當(dāng)時(shí)美國(guó)馬里蘭州的州長(zhǎng)夫人肯德爾·埃爾利希(Kendel Ehrlich)甚至公開對(duì)媒體表示:“如果我有機(jī)會(huì),真想一槍崩了布蘭妮。”以此指責(zé)布蘭妮給孩子們做了不良的榜樣,讓美國(guó)的媽媽們感到不安。

          在一次訪談中,主持人當(dāng)著布蘭妮的面播放了這段視頻,布蘭妮聽到后緊蹙雙眉,微微搖頭,“太可怕了……可是,我不是用來幫她們教育小孩的啊。”她忍不住哭了出來。

          在紀(jì)錄片《陷害布蘭妮》中,一位業(yè)內(nèi)人士提出,沒有一個(gè)男孩樂隊(duì)的成員會(huì)像布蘭妮這樣受到如此嚴(yán)格的道德審查。

          “讓我們直截了當(dāng)?shù)卣f:發(fā)生在布蘭妮·斯皮爾斯身上的事情,永遠(yuǎn)不會(huì)發(fā)生在一個(gè)男版的她身上。”專欄作家海琳·奧倫(Helaine Olen)在《華盛頓郵報(bào)》一針見血地寫道,“想想看,有多少男明星曾在公共場(chǎng)合做出過瘋狂的、情緒失控的、甚至磕了藥似的行為。幾秒內(nèi)我就能說出邁克爾·杰克遜、坎耶·韋斯特和小羅伯特·唐尼的名字。小羅伯特·唐尼曾被警察帶走,理由是他光著身子走進(jìn)鄰居家,躺在孩子的床上睡著了。當(dāng)鄰居撥打911時(shí),你甚至可以聽到他的鼾聲。還有布蘭妮的父親,杰米——一個(gè)非常不合時(shí)宜的男人,布蘭妮的前夫曾申請(qǐng)并獲得了限制令,禁止杰米靠近自己以及兩個(gè)孩子。”

          “管理學(xué)理論表明,人們會(huì)默認(rèn)男人是有能力的,不管他們的過去如何;而女性卻不得不一次又一次證明自己的工作能力、財(cái)務(wù)敏感、理智水平。” 海琳·奧倫繼續(xù)寫道,“縱觀歷史,監(jiān)護(hù)制度和非自愿承諾制度一直被用來控制女性的人身自由和財(cái)務(wù)自由。”

          紀(jì)錄片《陷害布蘭妮》截圖。一位曾經(jīng)合作過的演員在接受采訪時(shí)如此評(píng)論布蘭妮。

          回看當(dāng)時(shí)輿論對(duì)布蘭妮的圍觀,以及對(duì)她“精神病”的指控,都彌漫著濃厚的厭女氣息。莫伊拉·多納根(Moira Donegan)在《衛(wèi)報(bào)》寫道:“歷史上,許多人僅僅因?yàn)槌钟胁皇軞g迎的觀點(diǎn),或行為冒犯了普遍傳統(tǒng),就被認(rèn)為是瘋了。女性尤其深受其害。長(zhǎng)期以來,那些厭惡女性或覬覦女性財(cái)富的人,總能因?yàn)橐恍┪⒉蛔愕赖脑颍雅灾缚爻莎傋印!?/p>

          而在紀(jì)錄片《陷害布蘭妮》于今年2月5日上線之后,聯(lián)合國(guó)婦女署活動(dòng)家蒙羅·伯格多夫(Munroe Bergdorf)在社交媒體上嚴(yán)肅表態(tài):“布蘭妮為自己所取得的成就付出了太大的代價(jià)。當(dāng)時(shí)的社會(huì)選擇對(duì)精神疾病避而不談,不愿正視女性的自我風(fēng)貌,不知如何消解四處蔓延的厭女情緒。女性公眾人物被媒體拿來消費(fèi)。世人追捧你到制高點(diǎn),為的是最終能親手將你毀滅。”

          大眾審視與娛樂產(chǎn)業(yè):誰是施害者,誰是受害者?

          “世人追捧你到制高點(diǎn),為的是最終能親手將你毀滅。”這句話,可能道出了布蘭妮悲劇的本質(zhì)。

          首張個(gè)人專輯《…Baby One More Time》讓她一夜成名,全球銷量超過3000萬張,成為世界銷量最高的專輯之一。但水能載舟亦能覆舟,隨著喜愛而來的是大眾對(duì)她從頭到腳的審視,以及對(duì)她私生活永不饜足的窺探。

          布蘭妮的首張個(gè)人專輯《…Baby One More Time》封面。

          在布蘭妮10歲剛出道的一段視頻中,一位滿頭銀發(fā)的主持人問身高才到自己腰身的布蘭妮:“你有男朋友了嗎?……你可以考慮我。”而當(dāng)時(shí)的觀眾,并沒有人感到不適。

          年齡稍長(zhǎng)之后,布蘭妮遇到的采訪問題更加刻薄露骨:“你還是處女嗎?”“你知道所有人都在想著你的胸嗎?”而在2001年的一段電視采訪中(當(dāng)時(shí)布蘭妮只有19歲),主持人直言:“對(duì)許多人來說,你是一個(gè)矛盾體。一面是甜美、純潔、童貞,一面是只穿著內(nèi)衣的性感蕩婦。”布蘭妮只好尷尬回應(yīng):“不是‘只穿著內(nèi)衣’,在《滾石》的封面上是唯一一次。我在演出時(shí)不會(huì)。”

          攝影師大衛(wèi)·拉查佩爾(David LaChapelle)拍攝的這張著名照片登在了《滾石》的封面上,讓布蘭妮成為被公眾凝視的對(duì)象。

          一張布蘭妮的“黑照”,價(jià)值100萬美元,在紀(jì)錄片《陷害布蘭妮》中職業(yè)狗仔記者Daniel Ramos提到。重賞之下,必有勇夫。于是布蘭妮的私人生活也暴露在了長(zhǎng)槍短炮、層層包圍的鏡頭之下。

          在前文提及的布蘭妮在前夫家探視孩子未果后,正是Daniel Ramos步步緊逼、追問布蘭妮的心情,最終致使她情緒崩潰,揮舞雨傘砸向了他開來的車。在拍下了布蘭妮的失態(tài)照片后,他摸著車上坑坑洼洼的痕跡,喜笑顏開:“天啊,這次賺大了。”憑借幾次三番偷拍到布蘭妮形容憔悴、情緒失控或是剃成光頭的照片,Daniel Ramos賺得盆滿缽滿。

          多年以后,《紐約時(shí)報(bào)》紀(jì)錄片團(tuán)隊(duì)問這名娛樂記者:“你覺得自己影響了布蘭妮的生活嗎?”

          Daniel Ramos回答:“我不這么認(rèn)為。在她身邊工作了這么多年,她從來沒對(duì)我們說‘我不想理你們,別煩我了’。”

          團(tuán)隊(duì)反問:“她說過‘別煩我’吧?”

          Daniel Ramos想了片刻,回答道:“她是說過‘今天你能讓我一個(gè)人待著嗎?’,但并不代表說‘永遠(yuǎn)別煩我’啊。”

          紀(jì)錄片《陷害布蘭妮》截圖。直播采訪時(shí),主持人問布蘭妮:“你是不是希望那些狗仔隊(duì)都走開?”

          比起布蘭妮巔峰時(shí)期溫暖陽光的笑容,她低谷階段剃光的頭發(fā)、濃重的黑眼圈似乎更能勾起大眾的注意、引發(fā)人們的唏噓。

          《每日人物》主筆安小慶曾把布蘭妮的境遇與她筆下香港的“瘋女人們”進(jìn)行類比。在《香港為什么有那么多“瘋女人”? 》一文中,她寫道:

          藍(lán)潔瑛頂著“四大癲王“的封號(hào),與后來的“瘋女人“們——吳綺莉、吳卓林、關(guān)淑怡一起,以肉身的磨蝕和精神的苦痛,源源不斷地給香港社會(huì)供應(yīng)著日常運(yùn)轉(zhuǎn)所需的“瘋癲“樣本。比起“傳奇“,他們更熱衷狗血和瘋癲,尤其是那些從原有高階層跌落折墮的“瘋女人“的故事,因?yàn)檫@不僅能夠滿足世人獵奇的心態(tài),還能最大程度地警示所有的香港人、尤其是香港女人——在這個(gè)國(guó)際自由貿(mào)易港,這個(gè)頂級(jí)消費(fèi)社會(huì),貧窮是可恥的,階層墜落更是不可饒恕的。

          2021年3月,布蘭妮·斯皮爾斯在社交媒體上發(fā)了一段跳舞的視頻,配文提到,在《陷害布蘭妮》上線后,她足足“哭了兩個(gè)星期”,“我的人生總是被猜測(cè)、被注視、被審判。為了我的精神狀態(tài)(保持理智和穩(wěn)定),我需要在每個(gè)晚上跳舞,來感受野性地、作為一個(gè)人似的活著。我的整個(gè)人生都被表演在公眾面前。將脆弱托付給世界、展現(xiàn)給世界,真的需要很大的力量,因?yàn)槲铱偸潜粚徟小⒈晃耆瑁幻襟w搞得狼狽不堪,直到今天依然如此。”

          在《新周刊》的記者眼中,紀(jì)錄片《陷害布蘭妮》是對(duì)過去傷害布蘭妮的小報(bào)的一次清算,人們希望借此將這位昔日美國(guó)偶像從疑似泥沼般的生活里解救出來。

          但很難判斷,布蘭妮自己是否需要媒體的再一次巨大關(guān)注。對(duì)她而言,《陷害布蘭妮》到底是對(duì)她的拯救,還是再一次揭開她的疤痕?就像粉絲發(fā)起的“釋放布蘭妮”運(yùn)動(dòng),通過布蘭妮社交媒體上的蛛絲馬跡來解讀她的生活狀態(tài),從而得出她需要“被拯救”的結(jié)論,這一方面讓更多粉絲聯(lián)合起來,給予布蘭妮支持和力量,另一方面,也讓布蘭妮的隱私和傷痛暴露于公眾面前。

          紀(jì)錄片《陷害布蘭妮》截圖。

          “世界根本無權(quán)知道有關(guān)布蘭妮·斯皮爾斯的任何事情。但是,大眾和媒體卻喜歡對(duì)女性公眾人物生活中最私密的細(xì)節(jié)進(jìn)行關(guān)注和判斷。每一個(gè)粉絲、記者和媒體評(píng)論員都認(rèn)為自己有權(quán)了解和評(píng)判斯皮爾斯最私人的事情。這是一種令人不安的侵犯,和監(jiān)護(hù)本身帶來的控制似乎并沒有什么不同,”霍夫斯特拉大學(xué)(Hofstra University)公共關(guān)系副教授卡拉·阿拉莫(Kara Alaimo)發(fā)表在CNN的評(píng)論中寫道,“監(jiān)護(hù)是否必要,是圍繞布蘭妮·斯皮爾斯的心理健康和財(cái)務(wù)狀況展開的。這些都是她有權(quán)保密的問題。在美國(guó),關(guān)于這方面的隱私權(quán)規(guī)定非常明確。《紐約時(shí)報(bào)》和其他新聞媒體的報(bào)道并不違法,但很難把挖掘女性私生活理解為為了什么公共利益。”

          這意味著,在屏幕之前審視、關(guān)注甚至支持著布蘭妮的人們也并非全然置身事外,以至于在某種程度上成為了悲劇的幫兇。公眾號(hào)“看理想”則進(jìn)一步提出,這種審視不僅傷害了布蘭妮,也可能在傷害我們,在觀看明星被規(guī)訓(xùn)的同時(shí),我們自身也成了規(guī)訓(xùn)的對(duì)象——

          如果身為“監(jiān)視主體”的我們,總是在以自我感覺良好的傲慢態(tài)度和侵略性眼光來檢視著這些明星的話,也會(huì)在不知不覺間通過自我規(guī)訓(xùn)的方式,默然順從了偏見、刻板和節(jié)目所宣揚(yáng)的美學(xué)標(biāo)準(zhǔn)與行為規(guī)范。——想要安全逃脫這種“全景敞視機(jī)制”(panopticism)的控制,是絕無可能的。

          參考鏈接:

          1.紀(jì)錄片《陷害布蘭妮》(Framing Britney Spears),《紐約時(shí)報(bào)》團(tuán)隊(duì)拍攝

          2.https://www.forbes.com/sites/maddieberg/2021/06/23/britney-spears-full-statement-against-her-conservatorship/?sh=4cb8fb0421bd

          3.https://www.npr.org/2021/06/24/1009726455/britney-spears-conservatorship-how-thats-supposed-to-work

          4.https://www.nytimes.com/2021/07/01/us/politics/britney-spears-warren-casey-conservatorship.html

          5.https://www.foxnews.com/entertainment/free-britney-a-bipartisan-cause-on-capitol-hill-after-impassioned-testimony

          6.https://www.hrw.org/news/2021/06/26/britney-spearss-conservatorship-mirrors-reality-millions-disabilities

          7.https://www.cnn.com/2021/06/23/opinions/britney-spears-deserves-privacy-alaimo/index.html

          8. https://www.nytimes.com/2021/06/29/opinion/britney-spears-conservatorship.html

          9.https://www.washingtonpost.com/opinions/2021/06/26/britney-spears-conservatorship-mistreatment/

          10.《是誰陷害了“小甜甜”布蘭妮?》,看理想,2021-02-25,https://mp.weixin.qq.com/s/Cg02JcHn4uOpKWqEEhosKA

          11.《從小甜甜到“瘋女人”,誰在陷害布蘭妮?》,新周刊,2021-05-02,https://mp.weixin.qq.com/s/dhFRiUXByvhgvwCqFTcPrA

          12.《誰制造了小甜甜布蘭妮的悲劇?》,李孟蘇,三聯(lián)生活周刊,2021-03-10,https://mp.weixin.qq.com/s/G4_VB4xBSSTzET-qYsqgFw

          13.《香港為什么有那么多“瘋女人”?》,安小慶,每日人物,2018-08-19,https://mp.weixin.qq.com/s/K5fK_TIkH5VrTClPjBMG8g

          撰文 | 肖舒妍

          編輯 | 李永博;王青

          校對(duì) | 王心

          端開發(fā)者丨JavaScript

          實(shí)際需求中開始

          要求:

          • 此類繼承自 Date,擁有Date的所有屬性和對(duì)象

          • 此類可以自由拓展方法

          形象點(diǎn)描述,就是要求可以這樣:

          1. // 假設(shè)最終的類是 MyDate,有一個(gè)getTest拓展方法

          2. let date=newMyDate();

          3. // 調(diào)用Date的方法,輸出GMT絕對(duì)毫秒數(shù)

          4. console.log(date.getTime());

          5. // 調(diào)用拓展的方法,隨便輸出什么,譬如helloworld!

          6. console.log(date.getTest());

          于是,隨手用JS中經(jīng)典的組合寄生法寫了一個(gè)繼承,然后,剛準(zhǔn)備完美收工,一運(yùn)行,卻出現(xiàn)了以下的情景:

          但是的心情是這樣的: 囧

          以前也沒有遇到過類似的問題,然后自己嘗試著用其它方法,多次嘗試,均無果(不算暴力混合法的情況),其實(shí)回過頭來看,是因?yàn)樗悸沸缕妫瑧{空想不到,并不是原理上有多難。。。

          于是,借助強(qiáng)大的搜素引擎,搜集資料,最后,再自己總結(jié)了一番,才有了本文。

          正文開始前,各位看官可以先暫停往下讀,嘗試下,在不借助任何網(wǎng)絡(luò)資料的情況下,是否能實(shí)現(xiàn)上面的需求?(就以 10分鐘為限吧)

          分析問題的關(guān)鍵

          借助stackoverflow上的回答。

          經(jīng)典的繼承法有何問題

          先看看本文最開始時(shí)提到的經(jīng)典繼承法實(shí)現(xiàn),如下:

          1. /**

          2. * 經(jīng)典的js組合寄生繼承

          3. */

          4. functionMyDate() {

          5. Date.apply(this, arguments);

          6. this.abc=1;

          7. }

          8. functioninherits(subClass, superClass) {

          9. functionInner() {}

          10. Inner.prototype=superClass.prototype;

          11. subClass.prototype=newInner();

          12. subClass.prototype.constructor=subClass;

          13. }

          14. inherits(MyDate,Date);

          15. MyDate.prototype.getTest=function() {

          16. returnthis.getTime();

          17. };

          18. let date=newMyDate();

          19. console.log(date.getTest());

          20. 就是這段代碼?,這也是JavaScript高程(紅寶書)中推薦的一種,一直用,從未失手,結(jié)果現(xiàn)在馬失前蹄。。。

            我們?cè)倩仡櫹滤膱?bào)錯(cuò):

            再打印它的原型看看:

            怎么看都沒問題,因?yàn)榘凑赵玩溁厮菀?guī)則, Date的所有原型方法都可以通過 MyDate對(duì)象的原型鏈往上回溯到。再仔細(xì)看看,發(fā)現(xiàn)它的關(guān)鍵并不是找不到方法,而是 thisisnotaDateobject.

            嗯哼,也就是說,關(guān)鍵是:由于調(diào)用的對(duì)象不是Date的實(shí)例,所以不允許調(diào)用,就算是自己通過原型繼承的也不行。

            為什么無法被繼承?

            首先,看看 MDN上的解釋,上面有提到,JavaScript的日期對(duì)象只能通過 JavaScriptDate作為構(gòu)造函數(shù)來實(shí)例化。

            然后再看看stackoverflow上的回答:

            有提到, v8引擎底層代碼中有限制,如果調(diào)用對(duì)象的 [[Class]]不是 Date,則拋出錯(cuò)誤。

            總的來說,結(jié)合這兩點(diǎn),可以得出一個(gè)結(jié)論:要調(diào)用Date上方法的實(shí)例對(duì)象必須通過Date構(gòu)造出來,否則不允許調(diào)用Date的方法。

            該如何實(shí)現(xiàn)繼承?

            雖然原因找到了,但是問題仍然要解決啊,真的就沒辦法了么?當(dāng)然不是,事實(shí)上還是有不少實(shí)現(xiàn)的方法的。

            暴力混合法

            首先,說說說下暴力的混合法,它是下面這樣子的:

            說到底就是:內(nèi)部生成一個(gè) Date對(duì)象,然后此類暴露的方法中,把原有 Date中所有的方法都代理一遍,而且嚴(yán)格來說,這根本算不上繼承(都沒有原型鏈回溯)。

            ES5黑魔法

            然后,再看看ES5中如何實(shí)現(xiàn)?

            1. // 需要考慮polyfill情況

            2. Object.setPrototypeOf=Object.setPrototypeOf ||

            3. function(obj, proto) {

            4. obj.__proto__=proto;

            5. returnobj;

            6. };

            7. /**

            8. * 用了點(diǎn)技巧的繼承,實(shí)際上返回的是Date對(duì)象

            9. */

            10. functionMyDate() {

            11. // bind屬于Function.prototype,接收的參數(shù)是:object, param1, params2...

            12. vardateInst=new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

            13. // 更改原型指向,否則無法調(diào)用MyDate原型上的方法

            14. // ES6方案中,這里就是[[prototype]]這個(gè)隱式原型對(duì)象,在沒有標(biāo)準(zhǔn)以前就是__proto__

            15. Object.setPrototypeOf(dateInst,MyDate.prototype);

            16. dateInst.abc=1;

            17. returndateInst;

            18. }

            19. // 原型重新指回Date,否則根本無法算是繼承

            20. Object.setPrototypeOf(MyDate.prototype,Date.prototype);

            21. MyDate.prototype.getTest=functiongetTest() {

            22. returnthis.getTime();

            23. };

            24. let date=newMyDate();

            25. // 正常輸出,譬如1515638988725

            26. console.log(date.getTest());

            27. 一眼看上去不知所措?沒關(guān)系,先看下圖來理解:(原型鏈關(guān)系一目了然)

              可以看到,用的是非常巧妙的一種做法:

              正常繼承的情況如下:

              • newMyDate()返回實(shí)例對(duì)象 date是由 MyDate構(gòu)造的

              • 原型鏈回溯是: date(MyDate對(duì)象)-date.__proto__-MyDate.prototype-MyDate.prototype.__proto__-Date.prototype

              這種做法的繼承的情況如下:

              • newMyDate()返回實(shí)例對(duì)象 date是由 Date構(gòu)造的

              • 原型鏈回溯是: date(Date對(duì)象)-date.__proto__-MyDate.prototype-MyDate.prototype.__proto__-Date.prototype

              可以看出,關(guān)鍵點(diǎn)在于:

              • 構(gòu)造函數(shù)里返回了一個(gè)真正的 Date對(duì)象(由 Date構(gòu)造,所以有這些內(nèi)部類中的關(guān)鍵 [[Class]]標(biāo)志),所以它有調(diào)用 Date原型上方法的權(quán)利

              • 構(gòu)造函數(shù)里的Date對(duì)象的 [[ptototype]](對(duì)外,瀏覽器中可通過 __proto__訪問)指向 MyDate.prototype,然后 MyDate.prototype再指向 Date.prototype。

              所以最終的實(shí)例對(duì)象仍然能進(jìn)行正常的原型鏈回溯,回溯到原本Date的所有原型方法。

              這樣通過一個(gè)巧妙的欺騙技巧,就實(shí)現(xiàn)了完美的Date繼承。不過補(bǔ)充一點(diǎn), MDN上有提到盡量不要修改對(duì)象的 [[Prototype]],因?yàn)檫@樣可能會(huì)干涉到瀏覽器本身的優(yōu)化。如果你關(guān)心性能,你就不應(yīng)該在一個(gè)對(duì)象中修改它的 [[Prototype]]

              ES6大法

              當(dāng)然,除了上述的ES5實(shí)現(xiàn),ES6中也可以直接繼承(自帶支持繼承 Date),而且更為簡(jiǎn)單:

              1. classMyDateextendsDate{

              2. constructor() {

              3. super();

              4. this.abc=1;

              5. }

              6. getTest() {

              7. returnthis.getTime();

              8. }

              9. }

              10. let date=newMyDate();

              11. // 正常輸出,譬如1515638988725

              12. console.log(date.getTest());

              對(duì)比下ES5中的實(shí)現(xiàn),這個(gè)真的是簡(jiǎn)單的不行,直接使用ES6的Class語法就行了。而且,也可以正常輸出。

              注意:這里的正常輸出環(huán)境是直接用ES6運(yùn)行,不經(jīng)過babel打包,打包后實(shí)質(zhì)上是轉(zhuǎn)化成ES5的,所以效果完全不一樣。

              ES6寫法,然后Babel打包

              雖然說上述ES6大法是可以直接繼承Date的,但是,考慮到實(shí)質(zhì)上大部分的生產(chǎn)環(huán)境是: ES6+Babel

              直接這樣用ES6 + Babel是會(huì)出問題的。

              不信的話,可以自行嘗試下,Babel打包成ES5后代碼大致是這樣的:

              然后當(dāng)信心滿滿的開始用時(shí),會(huì)發(fā)現(xiàn):

              對(duì),又出現(xiàn)了這個(gè)問題,也許這時(shí)候是這樣的⊙?⊙

              因?yàn)檗D(zhuǎn)譯后的ES5源碼中,仍然是通過 MyDate來構(gòu)造,而 MyDate的構(gòu)造中又無法修改屬于 Date內(nèi)部的 [[Class]]之類的私有標(biāo)志,因此構(gòu)造出的對(duì)象仍然不允許調(diào)用 Date方法(調(diào)用時(shí),被引擎底層代碼識(shí)別為 [[Class]]標(biāo)志不符合,不允許調(diào)用,拋出錯(cuò)誤)。

              由此可見,ES6繼承的內(nèi)部實(shí)現(xiàn)和Babel打包編譯出來的實(shí)現(xiàn)是有區(qū)別的。(雖說Babel的polyfill一般會(huì)按照定義的規(guī)范去實(shí)現(xiàn)的,但也不要過度迷信)。

              幾種繼承的細(xì)微區(qū)別

              雖然上述提到的三種方法都可以達(dá)到繼承 Date的目的-混合法嚴(yán)格說不能算繼承,只不過是另類實(shí)現(xiàn)。

              于是,將所有能打印的主要信息都打印出來,分析幾種繼承的區(qū)別,大致場(chǎng)景是這樣的:

              可以參考:( 請(qǐng)進(jìn)入調(diào)試模式)https://dailc.github.io/fe-interview/demo/extends_date.html

              從上往下, 1,2,3,4四種繼承實(shí)現(xiàn)分別是:(排出了混合法)

              • ES6的Class大法

              • 經(jīng)典組合寄生繼承法

              • 本文中的取巧做法,Date構(gòu)造實(shí)例,然后更改 __proto__的那種

              • ES6的Class大法,Babel打包后的實(shí)現(xiàn)(無法正常調(diào)用的)

              1. ~~~~以下是MyDate們的prototype~~~~~~~~~

              2. Date{constructor: ?, getTest: ?}

              3. Date{constructor: ?, getTest: ?}

              4. Date{getTest: ?, constructor: ?}

              5. Date{constructor: ?, getTest: ?}

              6. ~~~~以下是new出的對(duì)象~~~~~~~~~

              7. SatJan13201821:58:55GMT+0800(CST)

              8. MyDate2{abc:1}

              9. SatJan13201821:58:55GMT+0800(CST)

              10. MyDate{abc:1}

              11. ~~~~以下是new出的對(duì)象的Object.prototype.toString.call~~~~~~~~~

              12. [objectDate]

              13. [objectObject]

              14. [objectDate]

              15. [objectObject]

              16. ~~~~以下是MyDate們的__proto__~~~~~~~~~

              17. ?Date() { [native code] }

              18. ? () { [native code] }

              19. ? () { [native code] }

              20. ?Date() { [native code] }

              21. ~~~~以下是new出的對(duì)象的__proto__~~~~~~~~~

              22. Date{constructor: ?, getTest: ?}

              23. Date{constructor: ?, getTest: ?}

              24. Date{getTest: ?, constructor: ?}

              25. Date{constructor: ?, getTest: ?}

              26. ~~~~以下是對(duì)象的__proto__與MyDate們的prototype比較~~~~~~~~~

              27. true

              28. true

              29. true

              30. true

              31. 看出,主要差別有幾點(diǎn):

                1. MyDate們的proto指向不一樣

                2. Object.prototype.toString.call的輸出不一樣

                3. 對(duì)象本質(zhì)不一樣,可以正常調(diào)用的 1,3都是 Date構(gòu)造出的,而其它的則是 MyDate構(gòu)造出的

                我們上文中得出的一個(gè)結(jié)論是:由于調(diào)用的對(duì)象不是由Date構(gòu)造出的實(shí)例,所以不允許調(diào)用,就算是自己的原型鏈上有Date.prototype也不行

                但是這里有兩個(gè)變量:分別是底層構(gòu)造實(shí)例的方法不一樣,以及對(duì)象的 Object.prototype.toString.call的輸出不一樣(另一個(gè) MyDate.__proto__可以排除,因?yàn)樵玩溁厮菘隙ㄅc它無關(guān))。

                萬一它的判斷是根據(jù) Object.prototype.toString.call來的呢?那這樣結(jié)論不就有誤差了?

                于是,根據(jù)ES6中的, Symbol.toStringTag,使用黑魔法,動(dòng)態(tài)的修改下它,排除下干擾:

                1. // 分別可以給date2,date3設(shè)置

                2. Object.defineProperty(date2,Symbol.toStringTag, {

                3. get:function() {

                4. returnDate;

                5. }

                6. });

                然后在打印下看看,變成這樣了:

                1. [objectDate]

                2. [objectDate]

                3. [objectDate]

                4. [objectObject]

                可以看到,第二個(gè)的 MyDate2構(gòu)造出的實(shí)例,雖然打印出來是 [objectDate],但是調(diào)用Date方法仍然是有錯(cuò)誤。

                此時(shí)我們可以更加準(zhǔn)確一點(diǎn)的確認(rèn):由于調(diào)用的對(duì)象不是由Date構(gòu)造出的實(shí)例,所以不允許調(diào)用。

                而且我們可以看到,就算通過黑魔法修改 Object.prototype.toString.call,內(nèi)部的 [[Class]]標(biāo)識(shí)位也是無法修改的。(這塊知識(shí)點(diǎn)大概是Object.prototype.toString.call可以輸出內(nèi)部的[[Class]],但無法改變它,由于不是重點(diǎn),這里不贅述)。

                ES6繼承與ES5繼承的區(qū)別

                從上午中的分析可以看到一點(diǎn):ES6的Class寫法繼承是沒問題的。但是換成ES5寫法就不行了。

                所以ES6的繼承大法和ES5肯定是有區(qū)別的,那么究竟是哪里不同呢?(主要是結(jié)合的本文繼承Date來說)

                區(qū)別:(以 SubClass, SuperClass, instance為例)

                ES5中繼承的實(shí)質(zhì)是:(那種經(jīng)典組合寄生繼承法)

                • 先由子類( SubClass)構(gòu)造出實(shí)例對(duì)象this

                • 然后在子類的構(gòu)造函數(shù)中,將父類( SuperClass)的屬性添加到 this上, SuperClass.apply(this,arguments)

                • 子類原型( SubClass.prototype)指向父類原型( SuperClass.prototype)

                • 所以 instance是子類( SubClass)構(gòu)造出的(所以沒有父類的 [[Class]]關(guān)鍵標(biāo)志)

                • 所以, instance有 SubClass和 SuperClass的所有實(shí)例屬性,以及可以通過原型鏈回溯,獲取 SubClass和 SuperClass原型上的方法

                ES6中繼承的實(shí)質(zhì)是:

                • 先由父類( SuperClass)構(gòu)造出實(shí)例對(duì)象this,這也是為什么必須先調(diào)用父類的 super()方法(子類沒有自己的this對(duì)象,需先由父類構(gòu)造)

                • 然后在子類的構(gòu)造函數(shù)中,修改this(進(jìn)行加工),譬如讓它指向子類原型( SubClass.prototype),這一步很關(guān)鍵,否則無法找到子類原型(注,子類構(gòu)造中加工這一步的實(shí)際做法是推測(cè)出的,從最終效果來推測(cè))

                • 然后同樣,子類原型( SubClass.prototype)指向父類原型( SuperClass.prototype)

                • 所以 instance是父類( SuperClass)構(gòu)造出的(所以有著父類的 [[Class]]關(guān)鍵標(biāo)志)

                • 所以, instance有 SubClass和 SuperClass的所有實(shí)例屬性,以及可以通過原型鏈回溯,獲取 SubClass和 SuperClass原型上的方法

                以上?就列舉了些重要信息,其它的如靜態(tài)方法的繼承沒有贅述。(靜態(tài)方法繼承實(shí)質(zhì)上只需要更改下 SubClass.__proto__到 SuperClass即可)

                可以看著這張圖快速理解:

                有沒有發(fā)現(xiàn)呢:ES6中的步驟和本文中取巧繼承Date的方法一模一樣,不同的是ES6是語言底層的做法,有它的底層優(yōu)化之處,而本文中的直接修改_proto_容易影響性能。

                ES6中在super中構(gòu)建this的好處?

                因?yàn)镋S6中允許我們繼承內(nèi)置的類,如Date,Array,Error等。如果this先被創(chuàng)建出來,在傳給Array等系統(tǒng)內(nèi)置類的構(gòu)造函數(shù),這些內(nèi)置類的構(gòu)造函數(shù)是不認(rèn)這個(gè)this的。所以需要現(xiàn)在super中構(gòu)建出來,這樣才能有著super中關(guān)鍵的 [[Class]]標(biāo)志,才能被允許調(diào)用。(否則就算繼承了,也無法調(diào)用這些內(nèi)置類的方法)

                構(gòu)造函數(shù)與實(shí)例對(duì)象

                看到這里,不知道是否對(duì)上午中頻繁提到的構(gòu)造函數(shù),實(shí)例對(duì)象有所混淆與困惑呢?這里稍微描述下。

                要弄懂這一點(diǎn),需要先知道 new一個(gè)對(duì)象到底發(fā)生了什么?先形象點(diǎn)說:

                new MyClass()中,都做了些什么工作
                1. functionMyClass() {

                2. this.abc=1;

                3. }

                4. MyClass.prototype.print=function() {

                5. console.log('this.abc:'+this.abc);

                6. };

                7. let instance=newMyClass();

                譬如,上述就是一個(gè)標(biāo)準(zhǔn)的實(shí)例對(duì)象生成,都發(fā)生了什么呢?

                步驟簡(jiǎn)述如下:(參考MDN,還有部分關(guān)于底層的描述略去-如[[Class]]標(biāo)識(shí)位等)

                1. 構(gòu)造函數(shù)內(nèi)部,創(chuàng)建一個(gè)新的對(duì)象,它繼承自 MyClass.prototype, letinstance=Object.create(MyClass.prototype);

                2. 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) MyClass,并將 this綁定到新創(chuàng)建的對(duì)象, MyClass.call(instance);,執(zhí)行后擁有所有實(shí)例屬性

                3. 如果構(gòu)造函數(shù)返回了一個(gè)“對(duì)象”,那么這個(gè)對(duì)象會(huì)取代整個(gè) new出來的結(jié)果。如果構(gòu)造函數(shù)沒有返回對(duì)象,那么new出來的結(jié)果為步驟1創(chuàng)建的對(duì)象。 (一般情況下構(gòu)造函數(shù)不返回任何值,不過用戶如果想覆蓋這個(gè)返回值,可以自己選擇返回一個(gè)普通對(duì)象來覆蓋。當(dāng)然,返回?cái)?shù)組也會(huì)覆蓋,因?yàn)閿?shù)組也是對(duì)象。)

                結(jié)合上述的描述,大概可以還原成以下代碼(簡(jiǎn)單還原,不考慮各種其它邏輯):

                1. let instance=Object.create(MyClass.prototype);

                2. let innerConstructReturn=MyClass.call(instance);

                3. let innerConstructReturnIsObj=typeofinnerConstructReturn==='object'||typeofinnerConstructReturn==='function';

                4. returninnerConstructReturnIsObj ? innerConstructReturn : instance;

                注意?:普通的函數(shù)構(gòu)建,可以簡(jiǎn)單的認(rèn)為就是上述步驟。實(shí)際上對(duì)于一些內(nèi)置類(如Date等),并沒有這么簡(jiǎn)單,還有一些自己的隱藏邏輯,譬如 [[Class]]標(biāo)識(shí)位等一些重要私有屬性。譬如可以在MDN上看到,以常規(guī)函數(shù)調(diào)用Date(即不加 new 操作符)將會(huì)返回一個(gè)字符串,而不是一個(gè)日期對(duì)象,如果這樣模擬的話會(huì)無效。

                覺得看起來比較繁瑣?可以看下圖梳理:

                那現(xiàn)在再回頭看看。

                什么是構(gòu)造函數(shù)?

                如上述中的 MyClass就是一個(gè)構(gòu)造函數(shù),在內(nèi)部它構(gòu)造出了 instance對(duì)象。

                什么是實(shí)例對(duì)象?

                instance就是一個(gè)實(shí)例對(duì)象,它是通過 new出來的?

                實(shí)例與構(gòu)造的關(guān)系

                有時(shí)候淺顯點(diǎn),可以認(rèn)為構(gòu)造函數(shù)是xxx就是xxx的實(shí)例。即:

                1. let instance=newMyClass();

                此時(shí)我們就可以認(rèn)為 instance是 MyClass的實(shí)例,因?yàn)樗臉?gòu)造函數(shù)就是它。

                實(shí)例就一定是由對(duì)應(yīng)的構(gòu)造函數(shù)構(gòu)造出的么?

                不一定,我們那ES5黑魔法來做示例。

                1. functionMyDate() {

                2. // bind屬于Function.prototype,接收的參數(shù)是:object, param1, params2...

                3. vardateInst=new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

                4. // 更改原型指向,否則無法調(diào)用MyDate原型上的方法

                5. // ES6方案中,這里就是[[prototype]]這個(gè)隱式原型對(duì)象,在沒有標(biāo)準(zhǔn)以前就是__proto__

                6. Object.setPrototypeOf(dateInst,MyDate.prototype);

                7. dateInst.abc=1;

                8. returndateInst;

                9. }

                10. 我們可以看到 instance的最終指向的原型是 MyDate.prototype,而 MyDate.prototype的構(gòu)造函數(shù)是 MyDate,因此可以認(rèn)為 instance是 MyDate的實(shí)例。

                  但是,實(shí)際上, instance卻是由 Date構(gòu)造的,我們可以繼續(xù)用 ES6中的 new.target來驗(yàn)證。

                  注意?:關(guān)于 new.target, MDN中的定義是:new.target返回一個(gè)指向構(gòu)造方法或函數(shù)的引用。

                  嗯哼,也就是說,返回的是構(gòu)造函數(shù)。

                  我們可以在相應(yīng)的構(gòu)造中測(cè)試打印:

                  1. classMyDateextendsDate{

                  2. constructor() {

                  3. super();

                  4. this.abc=1;

                  5. console.log('~~~new.target.name:MyDate~~~~');

                  6. console.log(new.target.name);

                  7. }

                  8. }

                  9. // new操作時(shí)的打印結(jié)果是:

                  10. // ~~~new.target.name:MyDate~~~~

                  11. // MyDate

                  然后,可以在上面的示例中看到,就算是ES6的Class繼承, MyDate構(gòu)造中打印 new.target也顯示 MyDate,但實(shí)際上它是由 Date來構(gòu)造(有著 Date關(guān)鍵的 [[Class]]標(biāo)志,因?yàn)槿绻皇荄ate構(gòu)造(如沒有標(biāo)志)是無法調(diào)用Date的方法的)。

                  這也算是一次小小的勘誤吧。

                  所以,實(shí)際上用 new.target是無法判斷實(shí)例對(duì)象到底是由哪一個(gè)構(gòu)造構(gòu)造的(這里指的是判斷底層真正的 [[Class]]標(biāo)志來源的構(gòu)造)。

                  再回到結(jié)論:實(shí)例對(duì)象不一定就是由它的原型上的構(gòu)造函數(shù)構(gòu)造的,有可能構(gòu)造函數(shù)內(nèi)部有著寄生等邏輯,偷偷的用另一個(gè)函數(shù)來構(gòu)造了下,當(dāng)然,簡(jiǎn)單情況下,我們直接說實(shí)例對(duì)象由對(duì)應(yīng)構(gòu)造函數(shù)構(gòu)造也沒錯(cuò)(不過,在涉及到這種Date之類的分析時(shí),我們還是得明白)。

                  [[Class]]與Internal slot

                  這一部分為補(bǔ)充內(nèi)容。

                  前文中一直提到一個(gè)概念:Date內(nèi)部的 [[Class]]標(biāo)識(shí)。

                  其實(shí),嚴(yán)格來說,不能這樣泛而稱之(前文中只是用這個(gè)概念是為了降低復(fù)雜度,便于理解),它可以分為以下兩部分:

                  在ES5中,每種內(nèi)置對(duì)象都定義了 [[Class]] 內(nèi)部屬性的值,[[Class]] 內(nèi)部屬性的值用于內(nèi)部區(qū)分對(duì)象的種類

                  • Object.prototype.toString訪問的就是這個(gè)[[Class]]

                  • 規(guī)范中除了通過 Object.prototype.toString,沒有提供任何手段使程序訪問此值。

                  • 而且Object.prototype.toString輸出無法被修改

                  而在ES5中,之前的 [[Class]] 不再使用,取而代之的是一系列的 internalslot

                  • Internal slot 對(duì)應(yīng)于與對(duì)象相關(guān)聯(lián)并由各種ECMAScript規(guī)范算法使用的內(nèi)部狀態(tài),它們沒有對(duì)象屬性,也不能被繼承

                  • 根據(jù)具體的 Internal slot 規(guī)范,這種狀態(tài)可以由任何ECMAScript語言類型或特定ECMAScript規(guī)范類型值的值組成

                  • 通過 Object.prototype.toString,仍然可以輸出Internal slot值

                  • 簡(jiǎn)單點(diǎn)理解(簡(jiǎn)化理解),Object.prototype.toString的流程是:如果是基本數(shù)據(jù)類型(除去Object以外的幾大類型),則返回原本的slot,如果是Object類型(包括內(nèi)置對(duì)象以及自己寫的對(duì)象),則調(diào)用 Symbol.toStringTag。 Symbol.toStringTag方法的默認(rèn)實(shí)現(xiàn)就是返回對(duì)象的Internal slot,這個(gè)方法可以被重寫

                  這兩點(diǎn)是有所差異的,需要區(qū)分(不過簡(jiǎn)單點(diǎn)可以統(tǒng)一理解為內(nèi)置對(duì)象內(nèi)部都有一個(gè)特殊標(biāo)識(shí),用來區(qū)分對(duì)應(yīng)類型-不符合類型就不給調(diào)用)。

                  JS內(nèi)置對(duì)象是這些:

                  1. Arguments,Array,Boolean,Date,Error,Function,JSON,Math,Number,Object,RegExp,String

                  ES6新增的一些,這里未提到:(如Promise對(duì)象可以輸出 [objectPromise]),而前文中提到的:

                  1. Object.defineProperty(date,Symbol.toStringTag, {

                  2. get:function() {

                  3. returnDate;

                  4. }

                  5. });

                  它的作用是重寫Symbol.toStringTag,截取date(雖然是內(nèi)置對(duì)象,但是仍然屬于Object)的 Object.prototype.toString的輸出,讓這個(gè)對(duì)象輸出自己修改后的 [objectDate]。

                  但是,僅僅是做到輸出的時(shí)候變成了Date,實(shí)際上內(nèi)部的 internalslot值并沒有被改變,因此仍然不被認(rèn)為是Date。

                  如何快速判斷是否繼承?

                  其實(shí),在判斷繼承時(shí),沒有那么多的技巧,就只有關(guān)鍵的一點(diǎn): [[prototype]]( __ptoto__)的指向關(guān)系。

                  譬如:

                  1. console.log(instanceinstanceofSubClass);

                  2. console.log(instanceinstanceofSuperClass);

                  實(shí)質(zhì)上就是:

                  • SubClass.prototype是否出現(xiàn)在 instance的原型鏈上

                  • SuperClass.prototype是否出現(xiàn)在 instance的原型鏈上

                  然后,對(duì)照本文中列舉的一些圖,一目了然就可以看清關(guān)系。有時(shí)候,完全沒有必要弄的太復(fù)雜。

                  覺得本文對(duì)你有幫助?請(qǐng)分享給更多人

                  前端開發(fā)者丨JavaScript


          主站蜘蛛池模板: 国产福利一区二区三区在线观看| 国产萌白酱在线一区二区| 久久se精品动漫一区二区三区| 亚洲成av人片一区二区三区| 亚洲成人一区二区| 99精品久久精品一区二区| 亚洲国产精品一区二区成人片国内 | 亚洲一区在线免费观看| 亚洲国产精品无码第一区二区三区| 成人一区专区在线观看| 99久久精品国产高清一区二区| 无码欧精品亚洲日韩一区| 一本一道波多野结衣一区| 免费观看日本污污ww网站一区| 中文字幕一区二区三区永久| 乱精品一区字幕二区| 国产精品av一区二区三区不卡蜜 | 亚洲国产精品一区第二页 | 亚洲色无码一区二区三区| 一区二区在线电影| 国产精品无码一区二区三区免费| 国产伦精品一区二区三区视频金莲| 日韩精品一区二区三区四区| 亚洲日韩一区二区一无码| 亚洲A∨精品一区二区三区| 无码人妻一区二区三区免费看| 久久久久人妻精品一区三寸| 日韩精品无码一区二区视频| 成人一区专区在线观看| 亚洲av福利无码无一区二区| 日韩伦理一区二区| 久久精品一区二区三区四区| 精品无码一区二区三区水蜜桃 | 国产伦精品一区二区三区女| 国产福利电影一区二区三区,免费久久久久久久精 | 一色一伦一区二区三区| 国精品无码A区一区二区| 日本一区二区三区爆乳| 国产精品无圣光一区二区 | 中日av乱码一区二区三区乱码| 国模大胆一区二区三区|